Imported Upstream version 2016.1
authorSimon McVittie <smcv@debian.org>
Wed, 27 Jan 2016 11:31:07 +0000 (12:31 +0100)
committerSimon McVittie <smcv@debian.org>
Wed, 27 Jan 2016 11:31:07 +0000 (12:31 +0100)
41 files changed:
bsdiff [deleted submodule]
bsdiff/.gitignore [new file with mode: 0644]
bsdiff/LICENSE [new file with mode: 0644]
bsdiff/Makefile-bsdiff.am [new file with mode: 0644]
bsdiff/Makefile.am [new file with mode: 0644]
bsdiff/README.md [new file with mode: 0644]
bsdiff/autogen.sh [new file with mode: 0755]
bsdiff/bsdiff.c [new file with mode: 0644]
bsdiff/bsdiff.h [new file with mode: 0644]
bsdiff/bspatch.c [new file with mode: 0644]
bsdiff/bspatch.h [new file with mode: 0644]
bsdiff/configure.ac [new file with mode: 0644]
libglnx [deleted submodule]
libglnx/.gitignore [new file with mode: 0644]
libglnx/COPYING [new file with mode: 0644]
libglnx/Makefile-libglnx.am [new file with mode: 0644]
libglnx/README.md [new file with mode: 0644]
libglnx/glnx-backport-autocleanups.h [new file with mode: 0644]
libglnx/glnx-backport-autoptr.h [new file with mode: 0644]
libglnx/glnx-backports.c [new file with mode: 0644]
libglnx/glnx-backports.h [new file with mode: 0644]
libglnx/glnx-console.c [new file with mode: 0644]
libglnx/glnx-console.h [new file with mode: 0644]
libglnx/glnx-dirfd.c [new file with mode: 0644]
libglnx/glnx-dirfd.h [new file with mode: 0644]
libglnx/glnx-errors.c [new file with mode: 0644]
libglnx/glnx-errors.h [new file with mode: 0644]
libglnx/glnx-fdio.c [new file with mode: 0644]
libglnx/glnx-fdio.h [new file with mode: 0644]
libglnx/glnx-libcontainer.c [new file with mode: 0644]
libglnx/glnx-libcontainer.h [new file with mode: 0644]
libglnx/glnx-local-alloc.c [new file with mode: 0644]
libglnx/glnx-local-alloc.h [new file with mode: 0644]
libglnx/glnx-lockfile.c [new file with mode: 0644]
libglnx/glnx-lockfile.h [new file with mode: 0644]
libglnx/glnx-shutil.c [new file with mode: 0644]
libglnx/glnx-shutil.h [new file with mode: 0644]
libglnx/glnx-xattrs.c [new file with mode: 0644]
libglnx/glnx-xattrs.h [new file with mode: 0644]
libglnx/libglnx.doap [new file with mode: 0644]
libglnx/libglnx.h [new file with mode: 0644]

diff --git a/bsdiff b/bsdiff
deleted file mode 160000 (submodule)
index 1edf9f6..0000000
--- a/bsdiff
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1edf9f656850c0c64dae260960fabd8249ea9c60
diff --git a/bsdiff/.gitignore b/bsdiff/.gitignore
new file mode 100644 (file)
index 0000000..4e13492
--- /dev/null
@@ -0,0 +1,11 @@
+.deps/*
+.libs/*
+*.lo
+*.o
+.dirstamp
+Makefile-bsdiff.am.inc
+AUTHORS
+NEWS
+README
+ChangeLog
+COPYING
diff --git a/bsdiff/LICENSE b/bsdiff/LICENSE
new file mode 100644 (file)
index 0000000..5d40665
--- /dev/null
@@ -0,0 +1,24 @@
+ Copyright 2003-2005 Colin Percival
+ Copyright 2012 Matthew Endsley
+ All rights reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted providing that the following conditions 
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
diff --git a/bsdiff/Makefile-bsdiff.am b/bsdiff/Makefile-bsdiff.am
new file mode 100644 (file)
index 0000000..1d810ed
--- /dev/null
@@ -0,0 +1,32 @@
+# Copyright (C) 2015 Giuseppe Scrivano <gscrivan@redhat.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted providing that the following conditions 
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+EXTRA_DIST += $(libbsdiff_srcpath)/bsdiff.h $(libbsdiff_srcpath)/bspatch.h $(libbsdiff_srcpath)/LICENSE $(libbsdiff_srcpath)/README.md
+
+libbsdiff_la_SOURCES = \
+       $(libbsdiff_srcpath)/bsdiff.c \
+       $(libbsdiff_srcpath)/bspatch.c \
+       $(NULL)
+
+libbsdiff_la_CFLAGS = $(AM_CFLAGS)
diff --git a/bsdiff/Makefile.am b/bsdiff/Makefile.am
new file mode 100644 (file)
index 0000000..abd3d7e
--- /dev/null
@@ -0,0 +1,11 @@
+bin_PROGRAMS = bsdiff bspatch
+
+bsdiff_SOURCES = bsdiff.c
+
+bspatch_SOURCES = bspatch.c
+
+bsdiff_CFLAGS = -DBSDIFF_EXECUTABLE
+bspatch_CFLAGS = -DBSPATCH_EXECUTABLE
+
+EXTRA_DIST = bsdiff.h bspatch.h
+
diff --git a/bsdiff/README.md b/bsdiff/README.md
new file mode 100644 (file)
index 0000000..a4b1d23
--- /dev/null
@@ -0,0 +1,129 @@
+bsdiff/bspatch
+==============
+bsdiff and bspatch are libraries for building and applying patches to binary
+files.
+
+The original algorithm and implementation was developed by Colin Percival.  The
+algorithm is detailed in his (unpublished) paper, [Naïve Differences of Executable Code](http://www.daemonology.net/papers/bsdiff.pdf).  For more information, visit his
+website at <http://www.daemonology.net/bsdiff/>.
+
+I maintain this project seperately from Colin's work, with the goal of making
+the core functionality easily embedable in existing projects.
+
+Contact
+-------
+[@MatthewEndsley](https://twitter.com/#!/MatthewEndsley)  
+<https://github.com/mendsley/bsdiff>
+
+License
+-------
+Copyright 2003-2005 Colin Percival  
+Copyright 2012 Matthew Endsley
+
+This project is governed by the BSD 2-clause license. For details see the file
+titled LICENSE in the project root folder.
+
+Overview
+--------
+There are two separate libraries in the project, bsdiff and bspatch. Each are
+self contained in bsdiff.c and bspatch.c The easiest way to integrate is to
+simply copy the c file to your source folder and build it.
+
+The overarching goal was to modify the original bsdiff/bspatch code from Colin
+and eliminate external dependencies and provide a simple interface to the core
+functionality.
+
+I've exposed relevant functions via the `_stream` classes. The only external
+dependency not exposed is `memcmp` in `bsdiff`.
+
+This library generates patches that are not compatible with the original bsdiff
+tool. The impompatibilities were motivated by the patching needs for the game
+AirMech <https://www.carbongames.com> and the following requirements:
+
+* Eliminate/minimize any seek operations when applying patches
+* Eliminate any required disk I/O and support embedded streams
+* Ability to easily embed the routines as a library instead of an external binary
+* Compile+run on all platforms we use to build the game (Windows, Linux, NaCl, OSX)
+
+Compiling
+---------
+The libraries should compile warning free in any moderately recent version of
+gcc. The project uses `<stdint.h>` which is technically a C99 file and not
+available in Microsoft Visual Studio. The easiest solution here is to use the
+msinttypes version of stdint.h from <https://code.google.com/p/msinttypes/>.
+The direct link for the lazy people is:
+<https://msinttypes.googlecode.com/svn/trunk/stdint.h>.
+
+If your compiler does not provide an implementation of `<stdint.h>` you can
+remove the header from the bsdiff/bspatch files and provide your own typedefs
+for the following symbols: `uint8_t`, `uint64_t` and `int64_t`.
+
+Examples
+--------
+Each project has an optional main function that serves as an example for using
+the library. Simply defined `BSDIFF_EXECUTABLE` or `BSPATCH_EXECUTABLE` to
+enable building the standalone tools.
+
+Reference
+---------
+### bsdiff
+
+       struct bsdiff_stream
+       {
+               void* opaque;
+               void* (*malloc)(size_t size);
+               void  (*free)(void* ptr);
+               int   (*write)(struct bsdiff_stream* stream,
+                                          const void* buffer, int size);
+       };
+
+       int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new,
+                  int64_t newsize, struct bsdiff_stream* stream);
+               
+
+In order to use `bsdiff`, you need to define functions for allocating memory and
+writing binary data. This behavior is controlled by the `stream` parameted
+passed to to `bsdiff(...)`.
+
+The `opaque` field is never read or modified from within the `bsdiff` function.
+The caller can use this field to store custom state data needed for the callback
+functions.
+
+The `malloc` and `free` members should point to functions that behave like the
+standard `malloc` and `free` C functions.
+
+The `write` function is called by bsdiff to write a block of binary data to the
+stream. The return value for `write` should be `0` on success and non-zero if
+the callback failed to write all data. In the default example, bzip2 is used to
+compress output data.
+
+`bsdiff` returns `0` on success and `-1` on failure.
+
+### bspatch
+
+       struct bspatch_stream
+       {
+               void* opaque;
+               int (*read)(const struct bspatch_stream* stream,
+                           void* buffer, int length);
+       };
+
+       int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new,
+                   int64_t newsize, struct bspatch_stream* stream);
+
+The `bspatch` function transforms the data for a file using data generated from
+`bsdiff`. The caller takes care of loading the old file and allocating space for
+new file data.  The `stream` parameter controls the process for reading binary
+patch data.
+
+The `opaque` field is never read or modified from within the bspatch function.
+The caller can use this field to store custom state data needed for the read
+function.
+
+The `read` function is called by `bspatch` to read a block of binary data from
+the stream.  The return value for `read` should be `0` on success and non-zero
+if the callback failed to read the requested amount of data. In the default
+example, bzip2 is used to decompress input data.
+
+`bspatch` returns `0` on success and `-1` on failure. On success, `new` contains
+the data for the patched file.
diff --git a/bsdiff/autogen.sh b/bsdiff/autogen.sh
new file mode 100755 (executable)
index 0000000..ce934ac
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+touch AUTHORS NEWS README ChangeLog
+cp LICENSE COPYING
+
+autoreconf -fis
diff --git a/bsdiff/bsdiff.c b/bsdiff/bsdiff.c
new file mode 100644 (file)
index 0000000..628f1c1
--- /dev/null
@@ -0,0 +1,445 @@
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions 
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bsdiff.h"
+
+#include <limits.h>
+#include <string.h>
+
+#define MIN(x,y) (((x)<(y)) ? (x) : (y))
+
+static void split(int64_t *I,int64_t *V,int64_t start,int64_t len,int64_t h)
+{
+       int64_t i,j,k,x,tmp,jj,kk;
+
+       if(len<16) {
+               for(k=start;k<start+len;k+=j) {
+                       j=1;x=V[I[k]+h];
+                       for(i=1;k+i<start+len;i++) {
+                               if(V[I[k+i]+h]<x) {
+                                       x=V[I[k+i]+h];
+                                       j=0;
+                               };
+                               if(V[I[k+i]+h]==x) {
+                                       tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
+                                       j++;
+                               };
+                       };
+                       for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
+                       if(j==1) I[k]=-1;
+               };
+               return;
+       };
+
+       x=V[I[start+len/2]+h];
+       jj=0;kk=0;
+       for(i=start;i<start+len;i++) {
+               if(V[I[i]+h]<x) jj++;
+               if(V[I[i]+h]==x) kk++;
+       };
+       jj+=start;kk+=jj;
+
+       i=start;j=0;k=0;
+       while(i<jj) {
+               if(V[I[i]+h]<x) {
+                       i++;
+               } else if(V[I[i]+h]==x) {
+                       tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
+                       j++;
+               } else {
+                       tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
+                       k++;
+               };
+       };
+
+       while(jj+j<kk) {
+               if(V[I[jj+j]+h]==x) {
+                       j++;
+               } else {
+                       tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
+                       k++;
+               };
+       };
+
+       if(jj>start) split(I,V,start,jj-start,h);
+
+       for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
+       if(jj==kk-1) I[jj]=-1;
+
+       if(start+len>kk) split(I,V,kk,start+len-kk,h);
+}
+
+static void qsufsort(int64_t *I,int64_t *V,const uint8_t *old,int64_t oldsize)
+{
+       int64_t buckets[256];
+       int64_t i,h,len;
+
+       for(i=0;i<256;i++) buckets[i]=0;
+       for(i=0;i<oldsize;i++) buckets[old[i]]++;
+       for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
+       for(i=255;i>0;i--) buckets[i]=buckets[i-1];
+       buckets[0]=0;
+
+       for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
+       I[0]=oldsize;
+       for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
+       V[oldsize]=0;
+       for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
+       I[0]=-1;
+
+       for(h=1;I[0]!=-(oldsize+1);h+=h) {
+               len=0;
+               for(i=0;i<oldsize+1;) {
+                       if(I[i]<0) {
+                               len-=I[i];
+                               i-=I[i];
+                       } else {
+                               if(len) I[i-len]=-len;
+                               len=V[I[i]]+1-i;
+                               split(I,V,i,len,h);
+                               i+=len;
+                               len=0;
+                       };
+               };
+               if(len) I[i-len]=-len;
+       };
+
+       for(i=0;i<oldsize+1;i++) I[V[i]]=i;
+}
+
+static int64_t matchlen(const uint8_t *old,int64_t oldsize,const uint8_t *new,int64_t newsize)
+{
+       int64_t i;
+
+       for(i=0;(i<oldsize)&&(i<newsize);i++)
+               if(old[i]!=new[i]) break;
+
+       return i;
+}
+
+static int64_t search(const int64_t *I,const uint8_t *old,int64_t oldsize,
+               const uint8_t *new,int64_t newsize,int64_t st,int64_t en,int64_t *pos)
+{
+       int64_t x,y;
+
+       if(en-st<2) {
+               x=matchlen(old+I[st],oldsize-I[st],new,newsize);
+               y=matchlen(old+I[en],oldsize-I[en],new,newsize);
+
+               if(x>y) {
+                       *pos=I[st];
+                       return x;
+               } else {
+                       *pos=I[en];
+                       return y;
+               }
+       };
+
+       x=st+(en-st)/2;
+       if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) {
+               return search(I,old,oldsize,new,newsize,x,en,pos);
+       } else {
+               return search(I,old,oldsize,new,newsize,st,x,pos);
+       };
+}
+
+static void offtout(int64_t x,uint8_t *buf)
+{
+       int64_t y;
+
+       if(x<0) y=-x; else y=x;
+
+       buf[0]=y%256;y-=buf[0];
+       y=y/256;buf[1]=y%256;y-=buf[1];
+       y=y/256;buf[2]=y%256;y-=buf[2];
+       y=y/256;buf[3]=y%256;y-=buf[3];
+       y=y/256;buf[4]=y%256;y-=buf[4];
+       y=y/256;buf[5]=y%256;y-=buf[5];
+       y=y/256;buf[6]=y%256;y-=buf[6];
+       y=y/256;buf[7]=y%256;
+
+       if(x<0) buf[7]|=0x80;
+}
+
+static int64_t writedata(struct bsdiff_stream* stream, const void* buffer, int64_t length)
+{
+       int64_t result = 0;
+
+       while (length > 0)
+       {
+               const int smallsize = (int)MIN(length, INT_MAX);
+               const int writeresult = stream->write(stream, buffer, smallsize);
+               if (writeresult == -1)
+               {
+                       return -1;
+               }
+
+               result += writeresult;
+               length -= smallsize;
+               buffer = (uint8_t*)buffer + smallsize;
+       }
+
+       return result;
+}
+
+struct bsdiff_request
+{
+       const uint8_t* old;
+       int64_t oldsize;
+       const uint8_t* new;
+       int64_t newsize;
+       struct bsdiff_stream* stream;
+       int64_t *I;
+       uint8_t *buffer;
+};
+
+static int bsdiff_internal(const struct bsdiff_request req)
+{
+       int64_t *I,*V;
+       int64_t scan,pos,len;
+       int64_t lastscan,lastpos,lastoffset;
+       int64_t oldscore,scsc;
+       int64_t s,Sf,lenf,Sb,lenb;
+       int64_t overlap,Ss,lens;
+       int64_t i;
+       uint8_t *buffer;
+       uint8_t buf[8 * 3];
+
+       if((V=req.stream->malloc((req.oldsize+1)*sizeof(int64_t)))==NULL) return -1;
+       I = req.I;
+
+       qsufsort(I,V,req.old,req.oldsize);
+       req.stream->free(V);
+
+       buffer = req.buffer;
+
+       /* Compute the differences, writing ctrl as we go */
+       scan=0;len=0;pos=0;
+       lastscan=0;lastpos=0;lastoffset=0;
+       while(scan<req.newsize) {
+               oldscore=0;
+
+               for(scsc=scan+=len;scan<req.newsize;scan++) {
+                       len=search(I,req.old,req.oldsize,req.new+scan,req.newsize-scan,
+                                       0,req.oldsize,&pos);
+
+                       for(;scsc<scan+len;scsc++)
+                       if((scsc+lastoffset<req.oldsize) &&
+                               (req.old[scsc+lastoffset] == req.new[scsc]))
+                               oldscore++;
+
+                       if(((len==oldscore) && (len!=0)) || 
+                               (len>oldscore+8)) break;
+
+                       if((scan+lastoffset<req.oldsize) &&
+                               (req.old[scan+lastoffset] == req.new[scan]))
+                               oldscore--;
+               };
+
+               if((len!=oldscore) || (scan==req.newsize)) {
+                       s=0;Sf=0;lenf=0;
+                       for(i=0;(lastscan+i<scan)&&(lastpos+i<req.oldsize);) {
+                               if(req.old[lastpos+i]==req.new[lastscan+i]) s++;
+                               i++;
+                               if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
+                       };
+
+                       lenb=0;
+                       if(scan<req.newsize) {
+                               s=0;Sb=0;
+                               for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
+                                       if(req.old[pos-i]==req.new[scan-i]) s++;
+                                       if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
+                               };
+                       };
+
+                       if(lastscan+lenf>scan-lenb) {
+                               overlap=(lastscan+lenf)-(scan-lenb);
+                               s=0;Ss=0;lens=0;
+                               for(i=0;i<overlap;i++) {
+                                       if(req.new[lastscan+lenf-overlap+i]==
+                                          req.old[lastpos+lenf-overlap+i]) s++;
+                                       if(req.new[scan-lenb+i]==
+                                          req.old[pos-lenb+i]) s--;
+                                       if(s>Ss) { Ss=s; lens=i+1; };
+                               };
+
+                               lenf+=lens-overlap;
+                               lenb-=lens;
+                       };
+
+                       offtout(lenf,buf);
+                       offtout((scan-lenb)-(lastscan+lenf),buf+8);
+                       offtout((pos-lenb)-(lastpos+lenf),buf+16);
+
+                       /* Write control data */
+                       if (writedata(req.stream, buf, sizeof(buf)))
+                               return -1;
+
+                       /* Write diff data */
+                       for(i=0;i<lenf;i++)
+                               buffer[i]=req.new[lastscan+i]-req.old[lastpos+i];
+                       if (writedata(req.stream, buffer, lenf))
+                               return -1;
+
+                       /* Write extra data */
+                       for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
+                               buffer[i]=req.new[lastscan+lenf+i];
+                       if (writedata(req.stream, buffer, (scan-lenb)-(lastscan+lenf)))
+                               return -1;
+
+                       lastscan=scan-lenb;
+                       lastpos=pos-lenb;
+                       lastoffset=pos-scan;
+               };
+       };
+
+       return 0;
+}
+
+int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream)
+{
+       int result;
+       struct bsdiff_request req;
+
+       if((req.I=stream->malloc((oldsize+1)*sizeof(int64_t)))==NULL)
+               return -1;
+
+       if((req.buffer=stream->malloc(newsize+1))==NULL)
+       {
+               stream->free(req.I);
+               return -1;
+       }
+
+       req.old = old;
+       req.oldsize = oldsize;
+       req.new = new;
+       req.newsize = newsize;
+       req.stream = stream;
+
+       result = bsdiff_internal(req);
+
+       stream->free(req.buffer);
+       stream->free(req.I);
+
+       return result;
+}
+
+#if defined(BSDIFF_EXECUTABLE)
+
+#include <sys/types.h>
+
+#include <bzlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int bz2_write(struct bsdiff_stream* stream, const void* buffer, int size)
+{
+       int bz2err;
+       BZFILE* bz2;
+
+       bz2 = (BZFILE*)stream->opaque;
+       BZ2_bzWrite(&bz2err, bz2, (void*)buffer, size);
+       if (bz2err != BZ_STREAM_END && bz2err != BZ_OK)
+               return -1;
+
+       return 0;
+}
+
+int main(int argc,char *argv[])
+{
+       int fd;
+       int bz2err;
+       uint8_t *old,*new;
+       off_t oldsize,newsize;
+       uint8_t buf[8];
+       FILE * pf;
+       struct bsdiff_stream stream;
+       BZFILE* bz2;
+
+       memset(&bz2, 0, sizeof(bz2));
+       stream.malloc = malloc;
+       stream.free = free;
+       stream.write = bz2_write;
+
+       if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
+
+       /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
+               that we never try to malloc(0) and get a NULL pointer */
+       if(((fd=open(argv[1],O_RDONLY,0))<0) ||
+               ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
+               ((old=malloc(oldsize+1))==NULL) ||
+               (lseek(fd,0,SEEK_SET)!=0) ||
+               (read(fd,old,oldsize)!=oldsize) ||
+               (close(fd)==-1)) err(1,"%s",argv[1]);
+
+
+       /* Allocate newsize+1 bytes instead of newsize bytes to ensure
+               that we never try to malloc(0) and get a NULL pointer */
+       if(((fd=open(argv[2],O_RDONLY,0))<0) ||
+               ((newsize=lseek(fd,0,SEEK_END))==-1) ||
+               ((new=malloc(newsize+1))==NULL) ||
+               (lseek(fd,0,SEEK_SET)!=0) ||
+               (read(fd,new,newsize)!=newsize) ||
+               (close(fd)==-1)) err(1,"%s",argv[2]);
+
+       /* Create the patch file */
+       if ((pf = fopen(argv[3], "w")) == NULL)
+               err(1, "%s", argv[3]);
+
+       /* Write header (signature+newsize)*/
+       offtout(newsize, buf);
+       if (fwrite("ENDSLEY/BSDIFF43", 16, 1, pf) != 1 ||
+               fwrite(buf, sizeof(buf), 1, pf) != 1)
+               err(1, "Failed to write header");
+
+
+       if (NULL == (bz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)))
+               errx(1, "BZ2_bzWriteOpen, bz2err=%d", bz2err);
+
+       stream.opaque = bz2;
+       if (bsdiff(old, oldsize, new, newsize, &stream))
+               err(1, "bsdiff");
+
+       BZ2_bzWriteClose(&bz2err, bz2, 0, NULL, NULL);
+       if (bz2err != BZ_OK)
+               err(1, "BZ2_bzWriteClose, bz2err=%d", bz2err);
+
+       if (fclose(pf))
+               err(1, "fclose");
+
+       /* Free the memory we used */
+       free(old);
+       free(new);
+
+       return 0;
+}
+
+#endif
diff --git a/bsdiff/bsdiff.h b/bsdiff/bsdiff.h
new file mode 100644 (file)
index 0000000..b37da5f
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions 
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSDIFF_H
+# define BSDIFF_H
+
+# include <stddef.h>
+# include <stdint.h>
+
+struct bsdiff_stream
+{
+       void* opaque;
+
+       void* (*malloc)(size_t size);
+       void (*free)(void* ptr);
+       int (*write)(struct bsdiff_stream* stream, const void* buffer, int size);
+};
+
+int bsdiff(const uint8_t* old, int64_t oldsize, const uint8_t* new, int64_t newsize, struct bsdiff_stream* stream);
+
+#endif
diff --git a/bsdiff/bspatch.c b/bsdiff/bspatch.c
new file mode 100644 (file)
index 0000000..881d7e3
--- /dev/null
@@ -0,0 +1,187 @@
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions 
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bspatch.h"
+
+static int64_t offtin(uint8_t *buf)
+{
+       int64_t y;
+
+       y=buf[7]&0x7F;
+       y=y*256;y+=buf[6];
+       y=y*256;y+=buf[5];
+       y=y*256;y+=buf[4];
+       y=y*256;y+=buf[3];
+       y=y*256;y+=buf[2];
+       y=y*256;y+=buf[1];
+       y=y*256;y+=buf[0];
+
+       if(buf[7]&0x80) y=-y;
+
+       return y;
+}
+
+int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, int64_t newsize, struct bspatch_stream* stream)
+{
+       uint8_t buf[8];
+       int64_t oldpos,newpos;
+       int64_t ctrl[3];
+       int64_t i;
+
+       oldpos=0;newpos=0;
+       while(newpos<newsize) {
+               /* Read control data */
+               for(i=0;i<=2;i++) {
+                       if (stream->read(stream, buf, 8))
+                               return -1;
+                       ctrl[i]=offtin(buf);
+               };
+
+               /* Sanity-check */
+               if(newpos+ctrl[0]>newsize)
+                       return -1;
+
+               /* Read diff string */
+               if (stream->read(stream, new + newpos, ctrl[0]))
+                       return -1;
+
+               /* Add old data to diff string */
+               for(i=0;i<ctrl[0];i++)
+                       if((oldpos+i>=0) && (oldpos+i<oldsize))
+                               new[newpos+i]+=old[oldpos+i];
+
+               /* Adjust pointers */
+               newpos+=ctrl[0];
+               oldpos+=ctrl[0];
+
+               /* Sanity-check */
+               if(newpos+ctrl[1]>newsize)
+                       return -1;
+
+               /* Read extra string */
+               if (stream->read(stream, new + newpos, ctrl[1]))
+                       return -1;
+
+               /* Adjust pointers */
+               newpos+=ctrl[1];
+               oldpos+=ctrl[2];
+       };
+
+       return 0;
+}
+
+#if defined(BSPATCH_EXECUTABLE)
+
+#include <bzlib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int bz2_read(const struct bspatch_stream* stream, void* buffer, int length)
+{
+       int n;
+       int bz2err;
+       BZFILE* bz2;
+
+       bz2 = (BZFILE*)stream->opaque;
+       n = BZ2_bzRead(&bz2err, bz2, buffer, length);
+       if (n != length)
+               return -1;
+
+       return 0;
+}
+
+int main(int argc,char * argv[])
+{
+       FILE * f;
+       int fd;
+       int bz2err;
+       uint8_t header[24];
+       uint8_t *old, *new;
+       int64_t oldsize, newsize;
+       BZFILE* bz2;
+       struct bspatch_stream stream;
+
+       if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
+
+       /* Open patch file */
+       if ((f = fopen(argv[3], "r")) == NULL)
+               err(1, "fopen(%s)", argv[3]);
+
+       /* Read header */
+       if (fread(header, 1, 24, f) != 24) {
+               if (feof(f))
+                       errx(1, "Corrupt patch\n");
+               err(1, "fread(%s)", argv[3]);
+       }
+
+       /* Check for appropriate magic */
+       if (memcmp(header, "ENDSLEY/BSDIFF43", 16) != 0)
+               errx(1, "Corrupt patch\n");
+
+       /* Read lengths from header */
+       newsize=offtin(header+16);
+       if(newsize<0)
+               errx(1,"Corrupt patch\n");
+
+       /* Close patch file and re-open it via libbzip2 at the right places */
+       if(((fd=open(argv[1],O_RDONLY,0))<0) ||
+               ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
+               ((old=malloc(oldsize+1))==NULL) ||
+               (lseek(fd,0,SEEK_SET)!=0) ||
+               (read(fd,old,oldsize)!=oldsize) ||
+               (close(fd)==-1)) err(1,"%s",argv[1]);
+       if((new=malloc(newsize+1))==NULL) err(1,NULL);
+
+       if (NULL == (bz2 = BZ2_bzReadOpen(&bz2err, f, 0, 0, NULL, 0)))
+               errx(1, "BZ2_bzReadOpen, bz2err=%d", bz2err);
+
+       stream.read = bz2_read;
+       stream.opaque = bz2;
+       if (bspatch(old, oldsize, new, newsize, &stream))
+               errx(1, "bspatch");
+
+       /* Clean up the bzip2 reads */
+       BZ2_bzReadClose(&bz2err, bz2);
+       fclose(f);
+
+       /* Write the new file */
+       if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
+               (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
+               err(1,"%s",argv[2]);
+
+       free(new);
+       free(old);
+
+       return 0;
+}
+
+#endif
diff --git a/bsdiff/bspatch.h b/bsdiff/bspatch.h
new file mode 100644 (file)
index 0000000..099c36e
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * Copyright 2012 Matthew Endsley
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions 
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BSPATCH_H
+# define BSPATCH_H
+
+# include <stdint.h>
+
+struct bspatch_stream
+{
+       void* opaque;
+       int (*read)(const struct bspatch_stream* stream, void* buffer, int length);
+};
+
+int bspatch(const uint8_t* old, int64_t oldsize, uint8_t* new, int64_t newsize, struct bspatch_stream* stream);
+
+#endif
+
diff --git a/bsdiff/configure.ac b/bsdiff/configure.ac
new file mode 100644 (file)
index 0000000..69de743
--- /dev/null
@@ -0,0 +1,30 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT([bsdiff], [0.1])
+AC_CONFIG_SRCDIR([bsdiff.c])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([1.9])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+# FIXME: Replace `main' with a function in `-lbz2':
+AC_CHECK_LIB([bz2], [BZ2_bzReadOpen])
+
+AC_CHECK_HEADERS([fcntl.h limits.h stddef.h stdint.h stdlib.h string.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT64_T
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([memset])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/libglnx b/libglnx
deleted file mode 160000 (submodule)
index 0313864..0000000
--- a/libglnx
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 03138641298fd6799f46b16423871f959332bacf
diff --git a/libglnx/.gitignore b/libglnx/.gitignore
new file mode 100644 (file)
index 0000000..55b946e
--- /dev/null
@@ -0,0 +1,16 @@
+# A path ostree writes to work around automake bug with
+# subdir-objects
+Makefile-libglnx.am.inc
+
+# Some standard bits
+.deps
+.libs
+.dirstamp
+*.typelib
+*.la
+*.lo
+*.o
+*.pyc
+*.stamp
+*~
+
diff --git a/libglnx/COPYING b/libglnx/COPYING
new file mode 100644 (file)
index 0000000..5bc8fb2
--- /dev/null
@@ -0,0 +1,481 @@
+                  GNU LIBRARY GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/libglnx/Makefile-libglnx.am b/libglnx/Makefile-libglnx.am
new file mode 100644 (file)
index 0000000..712e931
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright (C) 2015 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING
+
+libglnx_la_SOURCES = \
+       $(libglnx_srcpath)/glnx-backport-autocleanups.h \
+       $(libglnx_srcpath)/glnx-backport-autoptr.h \
+       $(libglnx_srcpath)/glnx-backports.h \
+       $(libglnx_srcpath)/glnx-backports.c \
+       $(libglnx_srcpath)/glnx-local-alloc.h \
+       $(libglnx_srcpath)/glnx-local-alloc.c \
+       $(libglnx_srcpath)/glnx-errors.h \
+       $(libglnx_srcpath)/glnx-errors.c \
+       $(libglnx_srcpath)/glnx-console.h \
+       $(libglnx_srcpath)/glnx-console.c \
+       $(libglnx_srcpath)/glnx-dirfd.h \
+       $(libglnx_srcpath)/glnx-dirfd.c \
+       $(libglnx_srcpath)/glnx-fdio.h \
+       $(libglnx_srcpath)/glnx-fdio.c \
+       $(libglnx_srcpath)/glnx-lockfile.h \
+       $(libglnx_srcpath)/glnx-lockfile.c \
+       $(libglnx_srcpath)/glnx-libcontainer.h \
+       $(libglnx_srcpath)/glnx-libcontainer.c \
+       $(libglnx_srcpath)/glnx-xattrs.h \
+       $(libglnx_srcpath)/glnx-xattrs.c \
+       $(libglnx_srcpath)/glnx-shutil.h \
+       $(libglnx_srcpath)/glnx-shutil.c \
+       $(libglnx_srcpath)/libglnx.h \
+       $(NULL)
+
+libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
+libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic 
+libglnx_la_LIBADD = $(libglnx_libs)
diff --git a/libglnx/README.md b/libglnx/README.md
new file mode 100644 (file)
index 0000000..50a5f90
--- /dev/null
@@ -0,0 +1,53 @@
+libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem
+
+It is for modules which depend on both GLib and Linux, intended to be
+used as a git submodule.
+
+Features:
+
+ - File APIs which use `openat()` like APIs, but also take a `GCancellable`
+   to support dynamic cancellation
+ - APIs also have a `GError` parameter
+ - High level "shutil", somewhat inspired by Python's
+ - A "console" API for tty output
+ - Some basic container utility functions
+ - A backport of the GLib cleanup macros for projects which can't yet take
+   a dependency on 2.40.
+
+Why?
+----
+
+There are multiple projects which have a hard dependency on Linux and
+GLib, such as NetworkManager, ostree, xdg-app, etc.  It makes sense
+for them to be able to share Linux-specific APIs.
+
+This module also contains some code taken from systemd, which has very
+high quality LGPLv2+ shared library code, but most of the internal
+shared library is private, and not namespaced.
+
+One could also compare this project to gnulib; the salient differences
+there are that at least some of this module is eventually destined for
+inclusion in GLib.
+
+Porting from libgsystem
+-----------------------
+
+For all of the filesystem access code, libglnx exposes only
+fd-relative API, not `GFile*`.  It does use `GCancellable` where
+applicable.
+
+For local allocation macros, you should start using the `g_auto`
+macros from GLib.  A backport is included in libglnx.  There are a few
+APIs not defined in GLib yet, such as `glnx_fd_close`.
+
+`gs_transfer_out_value` is replaced by `g_steal_pointer`.
+
+Contributing
+------------
+
+Currently there is not a Bugzilla product - one may be created
+in the future.  You can submit PRs against the Github mirror:
+
+https://github.com/GNOME/libglnx/pulls
+
+Or alternatively, email one of the maintainers directly.
diff --git a/libglnx/glnx-backport-autocleanups.h b/libglnx/glnx-backport-autocleanups.h
new file mode 100644 (file)
index 0000000..53f7ffd
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2015 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#pragma once
+
+#include <glnx-backport-autoptr.h>
+
+#if !GLIB_CHECK_VERSION(2, 43, 4)
+
+static inline void
+g_autoptr_cleanup_generic_gfree (void *p)
+{ 
+  void **pp = (void**)p;
+  if (*pp)
+    g_free (*pp);
+}
+
+static inline void
+g_autoptr_cleanup_gstring_free (GString *string)
+{
+  if (string)
+    g_string_free (string, TRUE);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GChecksum, g_checksum_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDateTime, g_date_time_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDir, g_dir_close)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GError, g_error_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHashTable, g_hash_table_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHmac, g_hmac_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOChannel, g_io_channel_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GKeyFile, g_key_file_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GList, g_list_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GArray, g_array_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GNode, g_node_destroy)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionContext, g_option_context_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionGroup, g_option_group_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPatternSpec, g_pattern_spec_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GQueue, g_queue_free)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GQueue, g_queue_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRand, g_rand_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRegex, g_regex_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMatchInfo, g_match_info_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GScanner, g_scanner_destroy)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSequence, g_sequence_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSList, g_slist_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free)
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimer, g_timer_destroy)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimeZone, g_time_zone_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTree, g_tree_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariant, g_variant_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantBuilder, g_variant_builder_unref)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantBuilder, g_variant_builder_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref)
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref)
+
+/* Add GObject-based types as needed. */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFile, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileEnumerator, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileIOStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInfo, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileMonitor, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref)
+
+#endif
diff --git a/libglnx/glnx-backport-autoptr.h b/libglnx/glnx-backport-autoptr.h
new file mode 100644 (file)
index 0000000..b36919d
--- /dev/null
@@ -0,0 +1,133 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Colin Walters <walters@verbum.org>
+ * 
+ * GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if !GLIB_CHECK_VERSION(2, 43, 4)
+
+#define _GLIB_AUTOPTR_FUNC_NAME(TypeName) glib_autoptr_cleanup_##TypeName
+#define _GLIB_AUTOPTR_TYPENAME(TypeName)  TypeName##_autoptr
+#define _GLIB_AUTO_FUNC_NAME(TypeName)    glib_auto_cleanup_##TypeName
+#define _GLIB_CLEANUP(func)               __attribute__((cleanup(func)))
+#define _GLIB_DEFINE_AUTOPTR_CHAINUP(ModuleObjName, ParentName) \
+  typedef ModuleObjName *_GLIB_AUTOPTR_TYPENAME(ModuleObjName);                                          \
+  static inline void _GLIB_AUTOPTR_FUNC_NAME(ModuleObjName) (ModuleObjName **_ptr) {                     \
+    _GLIB_AUTOPTR_FUNC_NAME(ParentName) ((ParentName **) _ptr); }                                        \
+
+
+/* these macros are API */
+#define G_DEFINE_AUTOPTR_CLEANUP_FUNC(TypeName, func) \
+  typedef TypeName *_GLIB_AUTOPTR_TYPENAME(TypeName);                                                           \
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS                                                                              \
+  static inline void _GLIB_AUTOPTR_FUNC_NAME(TypeName) (TypeName **_ptr) { if (*_ptr) (func) (*_ptr); }         \
+  G_GNUC_END_IGNORE_DEPRECATIONS
+#define G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(TypeName, func) \
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS                                                                              \
+  static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { (func) (_ptr); }                         \
+  G_GNUC_END_IGNORE_DEPRECATIONS
+#define G_DEFINE_AUTO_CLEANUP_FREE_FUNC(TypeName, func, none) \
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS                                                                              \
+  static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { if (*_ptr != none) (func) (*_ptr); }     \
+  G_GNUC_END_IGNORE_DEPRECATIONS
+#define g_autoptr(TypeName) _GLIB_CLEANUP(_GLIB_AUTOPTR_FUNC_NAME(TypeName)) _GLIB_AUTOPTR_TYPENAME(TypeName)
+#define g_auto(TypeName) _GLIB_CLEANUP(_GLIB_AUTO_FUNC_NAME(TypeName)) TypeName
+#define g_autofree _GLIB_CLEANUP(g_autoptr_cleanup_generic_gfree)
+
+/**
+ * g_steal_pointer:
+ * @pp: a pointer to a pointer
+ *
+ * Sets @pp to %NULL, returning the value that was there before.
+ *
+ * Conceptually, this transfers the ownership of the pointer from the
+ * referenced variable to the "caller" of the macro (ie: "steals" the
+ * reference).
+ *
+ * The return value will be properly typed, according to the type of
+ * @pp.
+ *
+ * This can be very useful when combined with g_autoptr() to prevent the
+ * return value of a function from being automatically freed.  Consider
+ * the following example (which only works on GCC and clang):
+ *
+ * |[
+ * GObject *
+ * create_object (void)
+ * {
+ *   g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL);
+ *
+ *   if (early_error_case)
+ *     return NULL;
+ *
+ *   return g_steal_pointer (&obj);
+ * }
+ * ]|
+ *
+ * It can also be used in similar ways for 'out' parameters and is
+ * particularly useful for dealing with optional out parameters:
+ *
+ * |[
+ * gboolean
+ * get_object (GObject **obj_out)
+ * {
+ *   g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL);
+ *
+ *   if (early_error_case)
+ *     return FALSE;
+ *
+ *   if (obj_out)
+ *     *obj_out = g_steal_pointer (&obj);
+ *
+ *   return TRUE;
+ * }
+ * ]|
+ *
+ * In the above example, the object will be automatically freed in the
+ * early error case and also in the case that %NULL was given for
+ * @obj_out.
+ *
+ * Since: 2.44
+ */
+static inline gpointer
+(g_steal_pointer) (gpointer pp)
+{
+  gpointer *ptr = (gpointer *) pp;
+  gpointer ref;
+
+  ref = *ptr;
+  *ptr = NULL;
+
+  return ref;
+}
+
+/* type safety */
+#define g_steal_pointer(pp) \
+  (0 ? (*(pp)) : (g_steal_pointer) (pp))
+
+#endif /* !GLIB_CHECK_VERSION(2, 43, 3) */
+
+G_END_DECLS
diff --git a/libglnx/glnx-backports.c b/libglnx/glnx-backports.c
new file mode 100644 (file)
index 0000000..c7bb600
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glnx-backports.h"
+
+#if !GLIB_CHECK_VERSION(2, 44, 0)
+gboolean
+glnx_strv_contains (const gchar * const *strv,
+                    const gchar         *str)
+{
+  g_return_val_if_fail (strv != NULL, FALSE);
+  g_return_val_if_fail (str != NULL, FALSE);
+
+  for (; *strv != NULL; strv++)
+    {
+      if (g_str_equal (str, *strv))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+glnx_set_object (GObject **object_ptr,
+                 GObject  *new_object)
+{
+  GObject *old_object = *object_ptr;
+
+  if (old_object == new_object)
+    return FALSE;
+
+  if (new_object != NULL)
+    g_object_ref (new_object);
+
+  *object_ptr = new_object;
+
+  if (old_object != NULL)
+    g_object_unref (old_object);
+
+  return TRUE;
+}
+#endif
diff --git a/libglnx/glnx-backports.h b/libglnx/glnx-backports.h
new file mode 100644 (file)
index 0000000..cd853cc
--- /dev/null
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2015 Colin Walters <walters@verbum.org>
+ * 
+ * GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if !GLIB_CHECK_VERSION(2, 44, 0)
+
+#define g_strv_contains glnx_strv_contains
+gboolean              glnx_strv_contains  (const gchar * const *strv,
+                                           const gchar         *str);
+
+#define g_set_object(object_ptr, new_object) \
+ (/* Check types match. */ \
+  0 ? *(object_ptr) = (new_object), FALSE : \
+  glnx_set_object ((GObject **) (object_ptr), (GObject *) (new_object)) \
+ )
+gboolean              glnx_set_object  (GObject **object_ptr,
+                                        GObject  *new_object);
+
+#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */
+
+G_END_DECLS
diff --git a/libglnx/glnx-console.c b/libglnx/glnx-console.c
new file mode 100644 (file)
index 0000000..deb4c86
--- /dev/null
@@ -0,0 +1,289 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glnx-console.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+static char *current_text = NULL;
+static gint current_percent = -1;
+static gboolean locked;
+
+static gboolean
+stdout_is_tty (void)
+{
+  static gsize initialized = 0;
+  static gboolean stdout_is_tty_v;
+
+  if (g_once_init_enter (&initialized))
+    {
+      stdout_is_tty_v = isatty (1);
+      g_once_init_leave (&initialized, 1);
+    }
+
+  return stdout_is_tty_v;
+}
+
+static volatile guint cached_columns = 0;
+static volatile guint cached_lines = 0;
+
+static int
+fd_columns (int fd)
+{
+  struct winsize ws = {};
+
+  if (ioctl (fd, TIOCGWINSZ, &ws) < 0)
+    return -errno;
+
+  if (ws.ws_col <= 0)
+    return -EIO;
+
+  return ws.ws_col;
+}
+
+/**
+ * glnx_console_columns:
+ * 
+ * Returns: The number of columns for terminal output
+ */
+guint
+glnx_console_columns (void)
+{
+  if (G_UNLIKELY (cached_columns == 0))
+    {
+      int c;
+
+      c = fd_columns (STDOUT_FILENO);
+
+      if (c <= 0)
+        c = 80;
+
+      if (c > 256)
+        c = 256;
+
+      cached_columns = c;
+    }
+
+  return cached_columns;
+}
+
+static int
+fd_lines (int fd)
+{
+  struct winsize ws = {};
+
+  if (ioctl (fd, TIOCGWINSZ, &ws) < 0)
+    return -errno;
+
+  if (ws.ws_row <= 0)
+    return -EIO;
+
+  return ws.ws_row;
+}
+
+/**
+ * glnx_console_lines:
+ * 
+ * Returns: The number of lines for terminal output
+ */
+guint
+glnx_console_lines (void)
+{
+  if (G_UNLIKELY (cached_lines == 0))
+    {
+      int l;
+
+      l = fd_lines (STDOUT_FILENO);
+
+      if (l <= 0)
+        l = 24;
+
+      cached_lines = l;
+    }
+
+  return cached_lines;
+}
+
+static void
+on_sigwinch (int signum)
+{
+  cached_columns = 0;
+  cached_lines = 0;
+}
+
+void
+glnx_console_lock (GLnxConsoleRef *console)
+{
+  static gsize sigwinch_initialized = 0;
+
+  g_return_if_fail (!locked);
+  g_return_if_fail (!console->locked);
+
+  console->is_tty = stdout_is_tty ();
+
+  locked = console->locked = TRUE;
+
+  current_percent = 0;
+
+  if (console->is_tty)
+    {
+      if (g_once_init_enter (&sigwinch_initialized))
+        {
+          signal (SIGWINCH, on_sigwinch);
+          g_once_init_leave (&sigwinch_initialized, 1);
+        }
+      
+      { static const char initbuf[] = { '\n', 0x1B, 0x37 };
+        (void) fwrite (initbuf, 1, sizeof (initbuf), stdout);
+      }
+    }
+}
+
+static void
+printpad (const char *padbuf,
+          guint       padbuf_len,
+          guint       n)
+{
+  const guint d = n / padbuf_len;
+  const guint r = n % padbuf_len;
+  guint i;
+
+  for (i = 0; i < d; i++)
+    fwrite (padbuf, 1, padbuf_len, stdout);
+  fwrite (padbuf, 1, r, stdout);
+}
+
+/**
+ * glnx_console_progress_text_percent:
+ * @text: Show this text before the progress bar
+ * @percentage: An integer in the range of 0 to 100
+ *
+ * On a tty, print to the console @text followed by an ASCII art
+ * progress bar whose percentage is @percentage.  If stdout is not a
+ * tty, a more basic line by line change will be printed.
+ *
+ * You must have called glnx_console_lock() before invoking this
+ * function.
+ *
+ */
+void
+glnx_console_progress_text_percent (const char *text,
+                                    guint percentage)
+{
+  static const char equals[] = "====================";
+  const guint n_equals = sizeof (equals) - 1;
+  static const char spaces[] = "                    ";
+  const guint n_spaces = sizeof (spaces) - 1;
+  const guint ncolumns = glnx_console_columns ();
+  const guint bar_min = 10;
+  const guint input_textlen = text ? strlen (text) : 0;
+  guint textlen;
+  guint barlen;
+
+  g_return_if_fail (percentage >= 0 && percentage <= 100);
+
+  if (text && !*text)
+    text = NULL;
+
+  if (percentage == current_percent
+      && g_strcmp0 (text, current_text) == 0)
+    return;
+
+  if (!stdout_is_tty ())
+    {
+      if (text)
+        fprintf (stdout, "%s", text);
+      if (percentage != -1)
+        {
+          if (text)
+            fputc (' ', stdout);
+          fprintf (stdout, "%u%%", percentage);
+        }
+      fputc ('\n', stdout);
+      fflush (stdout);
+      return;
+    }
+
+  if (ncolumns < bar_min)
+    return; /* TODO: spinner */
+
+  /* Restore cursor */
+  { const char beginbuf[2] = { 0x1B, 0x38 };
+    (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout);
+  }
+
+  textlen = MIN (input_textlen, ncolumns - bar_min);
+  barlen = ncolumns - textlen;
+
+  if (textlen > 0)
+    {
+      fwrite (text, 1, textlen - 1, stdout);
+      fputc (' ', stdout);
+    }
+  
+  { 
+    const guint nbraces = 2;
+    const guint textpercent_len = 5;
+    const guint bar_internal_len = barlen - nbraces - textpercent_len;
+    const guint eqlen = bar_internal_len * (percentage / 100.0);
+    const guint spacelen = bar_internal_len - eqlen; 
+
+    fputc ('[', stdout);
+    printpad (equals, n_equals, eqlen);
+    printpad (spaces, n_spaces, spacelen);
+    fputc (']', stdout);
+    fprintf (stdout, " %3d%%", percentage);
+  }
+
+  { const guint spacelen = ncolumns - textlen - barlen;
+    printpad (spaces, n_spaces, spacelen);
+  }
+
+  fflush (stdout);
+}
+
+/**
+ * glnx_console_unlock:
+ *
+ * Print a newline, and reset all cached console progress state.
+ *
+ * This function does nothing if stdout is not a tty.
+ */
+void
+glnx_console_unlock (GLnxConsoleRef *console)
+{
+  g_return_if_fail (locked);
+  g_return_if_fail (console->locked);
+
+  current_percent = -1;
+  g_clear_pointer (&current_text, g_free);
+
+  if (console->is_tty)
+    fputc ('\n', stdout);
+      
+  locked = FALSE;
+}
diff --git a/libglnx/glnx-console.h b/libglnx/glnx-console.h
new file mode 100644 (file)
index 0000000..9f620cc
--- /dev/null
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+
+G_BEGIN_DECLS
+
+struct GLnxConsoleRef {
+  gboolean locked;
+  gboolean is_tty;
+};
+
+typedef struct GLnxConsoleRef GLnxConsoleRef;
+
+void    glnx_console_lock (GLnxConsoleRef *ref);
+
+void    glnx_console_progress_text_percent (const char     *text,
+                                             guint           percentage);
+
+void    glnx_console_unlock (GLnxConsoleRef *ref);
+
+guint    glnx_console_lines (void);
+
+guint    glnx_console_columns (void);
+
+static inline void
+glnx_console_ref_cleanup (GLnxConsoleRef *p)
+{
+  glnx_console_unlock (p);
+}
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxConsoleRef, glnx_console_ref_cleanup)
+
+G_END_DECLS
diff --git a/libglnx/glnx-dirfd.c b/libglnx/glnx-dirfd.c
new file mode 100644 (file)
index 0000000..d126f15
--- /dev/null
@@ -0,0 +1,305 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glnx-dirfd.h>
+#include <glnx-errors.h>
+#include <glnx-local-alloc.h>
+
+/**
+ * glnx_opendirat_with_errno:
+ * @dfd: File descriptor for origin directory
+ * @name: Pathname, relative to @dfd
+ * @follow: Whether or not to follow symbolic links
+ *
+ * Use openat() to open a directory, using a standard set of flags.
+ * This function sets errno.
+ */
+int
+glnx_opendirat_with_errno (int           dfd,
+                           const char   *path,
+                           gboolean      follow)
+{
+  int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY;
+  if (!follow)
+    flags |= O_NOFOLLOW;
+
+  dfd = glnx_dirfd_canonicalize (dfd);
+
+  return openat (dfd, path, flags);
+}
+
+/**
+ * glnx_opendirat:
+ * @dfd: File descriptor for origin directory
+ * @path: Pathname, relative to @dfd
+ * @follow: Whether or not to follow symbolic links
+ * @error: Error
+ *
+ * Use openat() to open a directory, using a standard set of flags.
+ */
+gboolean
+glnx_opendirat (int             dfd,
+                const char     *path,
+                gboolean        follow,
+                int            *out_fd,
+                GError        **error)
+{
+  int ret = glnx_opendirat_with_errno (dfd, path, follow);
+  if (ret == -1)
+    {
+      glnx_set_prefix_error_from_errno (error, "%s", "openat");
+      return FALSE;
+    }
+  *out_fd = ret;
+  return TRUE;
+}
+
+struct GLnxRealDirfdIterator
+{
+  gboolean initialized;
+  int fd;
+  DIR *d;
+};
+typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator;
+
+/**
+ * glnx_dirfd_iterator_init_at:
+ * @dfd: File descriptor, may be AT_FDCWD or -1
+ * @path: Path, may be relative to @df
+ * @follow: If %TRUE and the last component of @path is a symlink, follow it
+ * @out_dfd_iter: (out caller-allocates): A directory iterator, will be initialized
+ * @error: Error
+ *
+ * Initialize @out_dfd_iter from @dfd and @path.
+ */
+gboolean
+glnx_dirfd_iterator_init_at (int                     dfd,
+                             const char             *path,
+                             gboolean                follow,
+                             GLnxDirFdIterator      *out_dfd_iter,
+                             GError                **error)
+{
+  gboolean ret = FALSE;
+  glnx_fd_close int fd = -1;
+  
+  if (!glnx_opendirat (dfd, path, follow, &fd, error))
+    goto out;
+
+  if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error))
+    goto out;
+  fd = -1; /* Transfer ownership */
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_dirfd_iterator_init_take_fd:
+ * @dfd: File descriptor - ownership is taken
+ * @dfd_iter: A directory iterator
+ * @error: Error
+ *
+ * Steal ownership of @dfd, using it to initialize @dfd_iter for
+ * iteration.
+ */
+gboolean
+glnx_dirfd_iterator_init_take_fd (int                dfd,
+                                  GLnxDirFdIterator *dfd_iter,
+                                  GError           **error)
+{
+  gboolean ret = FALSE;
+  GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
+  DIR *d = NULL;
+
+  d = fdopendir (dfd);
+  if (!d)
+    {
+      glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
+      goto out;
+    }
+
+  real_dfd_iter->fd = dfd;
+  real_dfd_iter->d = d;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_dirfd_iterator_next_dent:
+ * @dfd_iter: A directory iterator
+ * @out_dent: (out) (transfer none): Pointer to dirent; do not free
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read the next value from @dfd_iter, causing @out_dent to be
+ * updated.  If end of stream is reached, @out_dent will be set
+ * to %NULL, and %TRUE will be returned.
+ */
+gboolean
+glnx_dirfd_iterator_next_dent (GLnxDirFdIterator  *dfd_iter,
+                               struct dirent     **out_dent,
+                               GCancellable       *cancellable,
+                               GError             **error)
+{
+  gboolean ret = FALSE;
+  GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    goto out;
+
+  do
+    {
+      errno = 0;
+      *out_dent = readdir (real_dfd_iter->d);
+      if (*out_dent == NULL && errno != 0)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
+          goto out;
+        }
+    } while (*out_dent &&
+             (strcmp ((*out_dent)->d_name, ".") == 0 ||
+              strcmp ((*out_dent)->d_name, "..") == 0));
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_dirfd_iterator_clear:
+ * @dfd_iter: Iterator, will be de-initialized
+ *
+ * Unset @dfd_iter, freeing any resources.  If @dfd_iter is not
+ * initialized, do nothing.
+ */
+void
+glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter)
+{
+  GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
+  /* fd is owned by dfd_iter */
+  (void) closedir (real_dfd_iter->d);
+  real_dfd_iter->initialized = FALSE;
+}
+
+/**
+ * glnx_fdrel_abspath:
+ * @dfd: Directory fd
+ * @path: Path
+ *
+ * Turn a fd-relative pair into something that can be used for legacy
+ * APIs expecting absolute paths.
+ *
+ * This is Linux specific, and only valid inside this process (unless
+ * you set up the child process to have the exact same fd number, but
+ * don't try that).
+ */
+char *
+glnx_fdrel_abspath (int         dfd,
+                    const char *path)
+{
+  dfd = glnx_dirfd_canonicalize (dfd);
+  if (dfd == AT_FDCWD)
+    return g_strdup (path);
+  return g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path);
+}
+
+/**
+ * glnx_mkdtempat:
+ * @dfd: Directory fd
+ * @tmpl: (type filename): template directory name
+ * @mode: permissions to create the temporary directory with
+ * @error: Error
+ *
+ * Similar to g_mkdtemp_full, but using openat.
+ */
+gboolean
+glnx_mkdtempat (int dfd,
+                gchar *tmpl,
+                int mode,
+                GError **error)
+{
+  char *XXXXXX;
+  int count;
+  static const char letters[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+  static const int NLETTERS = sizeof (letters) - 1;
+  glong value;
+  GTimeVal tv;
+  static int counter = 0;
+
+  g_return_val_if_fail (tmpl != NULL, -1);
+
+  /* find the last occurrence of "XXXXXX" */
+  XXXXXX = g_strrstr (tmpl, "XXXXXX");
+
+  if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                   "Invalid temporary directory template '%s'", tmpl);
+      return FALSE;
+    }
+
+  /* Get some more or less random data.  */
+  g_get_current_time (&tv);
+  value = (tv.tv_usec ^ tv.tv_sec) + counter++;
+
+  for (count = 0; count < 100; value += 7777, ++count)
+    {
+      glong v = value;
+
+      /* Fill in the random bits.  */
+      XXXXXX[0] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[1] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[2] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[3] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[4] = letters[v % NLETTERS];
+      v /= NLETTERS;
+      XXXXXX[5] = letters[v % NLETTERS];
+
+      if (mkdirat (dfd, tmpl, mode) == -1)
+        {
+          if (errno == EEXIST)
+            continue;
+
+          /* Any other error will apply also to other names we might
+           *  try, and there are 2^32 or so of them, so give up now.
+           */
+          glnx_set_prefix_error_from_errno (error, "%s", "mkdirat");
+          return FALSE;
+        }
+
+      return TRUE;
+    }
+
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+               "mkstempat ran out of combinations to try.");
+  return FALSE;
+}
diff --git a/libglnx/glnx-dirfd.h b/libglnx/glnx-dirfd.h
new file mode 100644 (file)
index 0000000..5b7b77a
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+G_BEGIN_DECLS
+/**
+ * glnx_dirfd_canonicalize:
+ * @fd: A directory file descriptor
+ *
+ * It's often convenient in programs to use `-1` for "unassigned fd",
+ * and also because gobject-introspection doesn't support `AT_FDCWD`,
+ * libglnx honors `-1` to mean `AT_FDCWD`.  This small inline function
+ * canonicalizes `-1 -> AT_FDCWD`.
+ */
+static inline int
+glnx_dirfd_canonicalize (int fd)
+{
+  if (fd == -1)
+    return AT_FDCWD;
+  return fd;
+}
+
+struct GLnxDirFdIterator {
+  gboolean initialized;
+  int fd;
+  gpointer padding_data[4];
+};
+
+typedef struct GLnxDirFdIterator GLnxDirFdIterator;
+gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path,
+                                    gboolean follow,
+                                    GLnxDirFdIterator *dfd_iter, GError **error);
+gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error);
+gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator  *dfd_iter,
+                                        struct dirent     **out_dent,
+                                        GCancellable       *cancellable,
+                                        GError            **error);
+void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter);
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear)
+
+int glnx_opendirat_with_errno (int           dfd,
+                               const char   *path,
+                               gboolean      follow);
+
+gboolean glnx_opendirat (int             dfd,
+                         const char     *path,
+                         gboolean        follow,
+                         int            *out_fd,
+                         GError        **error);
+
+char *glnx_fdrel_abspath (int         dfd,
+                          const char *path);
+
+gboolean glnx_mkdtempat (int dfd,
+                         gchar *tmpl,
+                         int mode,
+                         GError **error);
+
+G_END_DECLS
diff --git a/libglnx/glnx-errors.c b/libglnx/glnx-errors.c
new file mode 100644 (file)
index 0000000..ab7bc0a
--- /dev/null
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glnx-backport-autocleanups.h>
+#include <glnx-errors.h>
+
+void
+glnx_real_set_prefix_error_from_errno (GError     **error,
+                                       gint         errsv,
+                                       const char  *format,
+                                       ...)
+{
+  if (!error)
+    return;
+  else
+    {
+      GString *buf = g_string_new ("");
+      va_list args;
+    
+      va_start (args, format);
+      g_string_append_vprintf (buf, format, args);
+      va_end (args);
+
+      g_string_append (buf, ": ");
+      g_string_append (buf, g_strerror (errsv));
+    
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           g_io_error_from_errno (errsv),
+                           buf->str);
+      g_string_free (buf, TRUE);
+      errno = errsv;
+    }
+}
diff --git a/libglnx/glnx-errors.h b/libglnx/glnx-errors.h
new file mode 100644 (file)
index 0000000..fffef6c
--- /dev/null
@@ -0,0 +1,49 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <errno.h>
+
+G_BEGIN_DECLS
+
+#define glnx_set_error_from_errno(error)                \
+  do {                                                  \
+    int errsv = errno;                                  \
+    g_set_error_literal (error, G_IO_ERROR,             \
+                         g_io_error_from_errno (errsv), \
+                         g_strerror (errsv));           \
+    errno = errsv;                                      \
+  } while (0);
+
+#define glnx_set_prefix_error_from_errno(error, format, args...)            \
+  do {                                                                  \
+    int errsv = errno;                                                  \
+    glnx_real_set_prefix_error_from_errno (error, errsv, format, args); \
+    errno = errsv;                                                      \
+  } while (0);
+
+void glnx_real_set_prefix_error_from_errno (GError     **error,
+                                            gint         errsv,
+                                            const char  *format,
+                                            ...) G_GNUC_PRINTF (3,4);
+
+G_END_DECLS
diff --git a/libglnx/glnx-fdio.c b/libglnx/glnx-fdio.c
new file mode 100644 (file)
index 0000000..7db33c4
--- /dev/null
@@ -0,0 +1,750 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * Portions derived from systemd:
+ *  Copyright 2010 Lennart Poettering
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/ioctl.h>
+#include <sys/sendfile.h>
+#include <errno.h>
+/* See linux.git/fs/btrfs/ioctl.h */
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
+
+#include <glnx-fdio.h>
+#include <glnx-dirfd.h>
+#include <glnx-errors.h>
+#include <glnx-xattrs.h>
+#include <glnx-backport-autoptr.h>
+#include <glnx-local-alloc.h>
+
+static guint8*
+glnx_fd_readall_malloc (int               fd,
+                        gsize            *out_len,
+                        gboolean          nul_terminate,
+                        GCancellable     *cancellable,
+                        GError          **error)
+{
+  gboolean success = FALSE;
+  const guint maxreadlen = 4096;
+  int res;
+  struct stat stbuf;
+  guint8* buf = NULL;
+  gsize buf_allocated;
+  gsize buf_size = 0;
+  gssize bytes_read;
+
+  do
+    res = fstat (fd, &stbuf);
+  while (G_UNLIKELY (res == -1 && errno == EINTR));
+  if (res == -1)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0)
+    buf_allocated = stbuf.st_size;
+  else
+    buf_allocated = 16;
+        
+  buf = g_malloc (buf_allocated);
+
+  while (TRUE)
+    {
+      gsize readlen = MIN (buf_allocated - buf_size, maxreadlen);
+      
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto out;
+
+      do
+        bytes_read = read (fd, buf + buf_size, readlen);
+      while (G_UNLIKELY (bytes_read == -1 && errno == EINTR));
+      if (G_UNLIKELY (bytes_read == -1))
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+      if (bytes_read == 0)
+        break;
+      
+      buf_size += bytes_read;
+      if (buf_allocated - buf_size < maxreadlen)
+        buf = g_realloc (buf, buf_allocated *= 2);
+    }
+
+  if (nul_terminate)
+    {
+      if (buf_allocated - buf_size == 0)
+        buf = g_realloc (buf, buf_allocated + 1);
+      buf[buf_size] = '\0';
+    }
+
+  success = TRUE;
+ out:
+  if (success)
+    {
+      *out_len = buf_size;
+      return buf;
+    }
+  g_free (buf);
+  return NULL;
+}
+
+/**
+ * glnx_fd_readall_bytes:
+ * @fd: A file descriptor
+ * @cancellable: Cancellable:
+ * @error: Error
+ *
+ * Read all data from file descriptor @fd into a #GBytes.  It's
+ * recommended to only use this for small files.
+ *
+ * Returns: (transfer full): A newly allocated #GBytes
+ */
+GBytes *
+glnx_fd_readall_bytes (int               fd,
+                       GCancellable     *cancellable,
+                       GError          **error)
+{
+  guint8 *buf;
+  gsize len;
+  
+  buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error);
+  if (!buf)
+    return NULL;
+  
+  return g_bytes_new_take (buf, len);
+}
+
+/**
+ * glnx_fd_readall_utf8:
+ * @fd: A file descriptor
+ * @out_len: (out): Returned length
+ * @cancellable: Cancellable:
+ * @error: Error
+ *
+ * Read all data from file descriptor @fd, validating
+ * the result as UTF-8.
+ *
+ * Returns: (transfer full): A string validated as UTF-8, or %NULL on error.
+ */
+char *
+glnx_fd_readall_utf8 (int               fd,
+                      gsize            *out_len,
+                      GCancellable     *cancellable,
+                      GError          **error)
+{
+  gboolean success = FALSE;
+  guint8 *buf;
+  gsize len;
+  
+  buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error);
+  if (!buf)
+    goto out;
+
+  if (!g_utf8_validate ((char*)buf, len, NULL))
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_DATA,
+                   "Invalid UTF-8");
+      goto out;
+    }
+
+  success = TRUE;
+ out:
+  if (success)
+    {
+      if (out_len)
+        *out_len = len;
+      return (char*)buf;
+    }
+  g_free (buf);
+  return NULL;
+}
+
+/**
+ * glnx_file_get_contents_utf8_at:
+ * @dfd: Directory file descriptor
+ * @subpath: Path relative to @dfd
+ * @out_len: (out) (allow-none): Optional length
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read the entire contents of the file referred
+ * to by @dfd and @subpath, validate the result as UTF-8.
+ * The length is optionally stored in @out_len.
+ *
+ * Returns: (transfer full): UTF-8 validated text, or %NULL on error
+ */
+char *
+glnx_file_get_contents_utf8_at (int                   dfd,
+                                const char           *subpath,
+                                gsize                *out_len,
+                                GCancellable         *cancellable,
+                                GError              **error)
+{
+  gboolean success = FALSE;
+  glnx_fd_close int fd = -1;
+  char *buf = NULL;
+  gsize len;
+
+  dfd = glnx_dirfd_canonicalize (dfd);
+
+  do
+    fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+  while (G_UNLIKELY (fd == -1 && errno == EINTR));
+  if (G_UNLIKELY (fd == -1))
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
+  if (G_UNLIKELY(!buf))
+    goto out;
+  
+  success = TRUE;
+ out:
+  if (success)
+    {
+      if (out_len)
+        *out_len = len;
+      return buf;
+    }
+  g_free (buf);
+  return NULL;
+}
+
+/**
+ * glnx_readlinkat_malloc:
+ * @dfd: Directory file descriptor
+ * @subpath: Subpath
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read the value of a symlink into a dynamically
+ * allocated buffer.
+ */
+char *
+glnx_readlinkat_malloc (int            dfd,
+                        const char    *subpath,
+                        GCancellable  *cancellable,
+                        GError       **error)
+{
+  size_t l = 100;
+
+  dfd = glnx_dirfd_canonicalize (dfd);
+
+  for (;;)
+    {
+      char *c;
+      ssize_t n;
+
+      c = g_malloc (l);
+      n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1));
+      if (n < 0)
+        {
+          glnx_set_error_from_errno (error);
+          g_free (c);
+          return FALSE;
+        }
+
+      if ((size_t) n < l-1)
+        {
+          c[n] = 0;
+          return c;
+        }
+
+      g_free (c);
+      l *= 2;
+    }
+
+  g_assert_not_reached ();
+}
+
+static gboolean
+copy_symlink_at (int                   src_dfd,
+                 const char           *src_subpath,
+                 const struct stat    *src_stbuf,
+                 int                   dest_dfd,
+                 const char           *dest_subpath,
+                 GLnxFileCopyFlags     copyflags,
+                 GCancellable         *cancellable,
+                 GError              **error)
+{
+  gboolean ret = FALSE;
+  g_autofree char *buf = NULL;
+
+  buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error);
+  if (!buf)
+    goto out;
+
+  if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+  
+  if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
+    {
+      g_autoptr(GVariant) xattrs = NULL;
+
+      if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs,
+                                         cancellable, error))
+        goto out;
+
+      if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs,
+                                         cancellable, error))
+        goto out;
+    }
+  
+  if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath,
+                                    src_stbuf->st_uid, src_stbuf->st_gid,
+                                    AT_SYMLINK_NOFOLLOW)) != 0)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+#define COPY_BUFFER_SIZE (16*1024)
+
+/* From systemd */
+
+static int btrfs_reflink(int infd, int outfd) {
+        int r;
+
+        g_return_val_if_fail(infd >= 0, -1);
+        g_return_val_if_fail(outfd >= 0, -1);
+
+        r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+static int loop_write(int fd, const void *buf, size_t nbytes) {
+        const uint8_t *p = buf;
+
+        g_return_val_if_fail(fd >= 0, -1);
+        g_return_val_if_fail(buf, -1);
+
+        errno = 0;
+
+        while (nbytes > 0) {
+                ssize_t k;
+
+                k = write(fd, p, nbytes);
+                if (k < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+                }
+
+                if (k == 0) /* Can't really happen */
+                        return -EIO;
+
+                p += k;
+                nbytes -= k;
+        }
+
+        return 0;
+}
+
+static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
+        bool try_sendfile = true;
+        int r;
+
+        g_return_val_if_fail (fdf >= 0, -1);
+        g_return_val_if_fail (fdt >= 0, -1);
+
+        /* Try btrfs reflinks first. */
+        if (try_reflink && max_bytes == (off_t) -1) {
+                r = btrfs_reflink(fdf, fdt);
+                if (r >= 0)
+                        return r;
+        }
+
+        for (;;) {
+                size_t m = COPY_BUFFER_SIZE;
+                ssize_t n;
+
+                if (max_bytes != (off_t) -1) {
+
+                        if (max_bytes <= 0)
+                                return -EFBIG;
+
+                        if ((off_t) m > max_bytes)
+                                m = (size_t) max_bytes;
+                }
+
+                /* First try sendfile(), unless we already tried */
+                if (try_sendfile) {
+
+                        n = sendfile(fdt, fdf, NULL, m);
+                        if (n < 0) {
+                                if (errno != EINVAL && errno != ENOSYS)
+                                        return -errno;
+
+                                try_sendfile = false;
+                                /* use fallback below */
+                        } else if (n == 0) /* EOF */
+                                break;
+                        else if (n > 0)
+                                /* Succcess! */
+                                goto next;
+                }
+
+                /* As a fallback just copy bits by hand */
+                {
+                        char buf[m];
+
+                        n = read(fdf, buf, m);
+                        if (n < 0)
+                                return -errno;
+                        if (n == 0) /* EOF */
+                                break;
+
+                        r = loop_write(fdt, buf, (size_t) n);
+                        if (r < 0)
+                                return r;
+                }
+
+        next:
+                if (max_bytes != (off_t) -1) {
+                        g_assert(max_bytes >= n);
+                        max_bytes -= n;
+                }
+        }
+
+        return 0;
+}
+
+/**
+ * glnx_file_copy_at:
+ * @src_dfd: Source directory fd
+ * @src_subpath: Subpath relative to @src_dfd
+ * @dest_dfd: Target directory fd
+ * @dest_subpath: Destination name
+ * @copyflags: Flags
+ * @cancellable: cancellable
+ * @error: Error
+ *
+ * Perform a full copy of the regular file or
+ * symbolic link from @src_subpath to @dest_subpath.
+ *
+ * If @src_subpath is anything other than a regular
+ * file or symbolic link, an error will be returned.
+ */
+gboolean
+glnx_file_copy_at (int                   src_dfd,
+                   const char           *src_subpath,
+                   struct stat          *src_stbuf,
+                   int                   dest_dfd,
+                   const char           *dest_subpath,
+                   GLnxFileCopyFlags     copyflags,
+                   GCancellable         *cancellable,
+                   GError              **error)
+{
+  gboolean ret = FALSE;
+  int r;
+  int dest_open_flags;
+  struct timespec ts[2];
+  glnx_fd_close int src_fd = -1;
+  glnx_fd_close int dest_fd = -1;
+  struct stat local_stbuf;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    goto out;
+
+  src_dfd = glnx_dirfd_canonicalize (src_dfd);
+  dest_dfd = glnx_dirfd_canonicalize (dest_dfd);
+
+  /* Automatically do stat() if no stat buffer was supplied */
+  if (!src_stbuf)
+    {
+      if (fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW) != 0)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+      src_stbuf = &local_stbuf;
+    }
+
+  if (S_ISLNK (src_stbuf->st_mode))
+    {
+      return copy_symlink_at (src_dfd, src_subpath, src_stbuf,
+                              dest_dfd, dest_subpath,
+                              copyflags,
+                              cancellable, error);
+    }
+  else if (!S_ISREG (src_stbuf->st_mode))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Cannot copy non-regular/non-symlink file: %s", src_subpath);
+      goto out;
+    }
+
+  src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW));
+  if (src_fd == -1)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY;
+  if (!(copyflags & GLNX_FILE_COPY_OVERWRITE))
+    dest_open_flags |= O_EXCL;
+  else
+    dest_open_flags |= O_TRUNC;
+
+  dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags, src_stbuf->st_mode));
+  if (dest_fd == -1)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE);
+  if (r < 0)
+    {
+      errno = -r;
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  if (fchown (dest_fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  if (fchmod (dest_fd, src_stbuf->st_mode & 07777) != 0)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  ts[0] = src_stbuf->st_atim;
+  ts[1] = src_stbuf->st_mtim;
+  (void) futimens (dest_fd, ts);
+
+  if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
+    {
+      g_autoptr(GVariant) xattrs = NULL;
+
+      if (!glnx_fd_get_all_xattrs (src_fd, &xattrs,
+                                   cancellable, error))
+        goto out;
+
+      if (!glnx_fd_set_all_xattrs (dest_fd, xattrs,
+                                   cancellable, error))
+        goto out;
+    }
+
+  if (copyflags & GLNX_FILE_COPY_DATASYNC)
+    {
+      if (fdatasync (dest_fd) < 0)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+  
+  r = close (dest_fd);
+  dest_fd = -1;
+  if (r < 0)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (!ret)
+    (void) unlinkat (dest_dfd, dest_subpath, 0);
+  return ret;
+}
+
+/**
+ * glnx_file_replace_contents_at:
+ * @dfd: Directory fd
+ * @subpath: Subpath
+ * @buf: (array len=len) (element-type guint8): File contents
+ * @len: Length (if `-1`, assume @buf is `NUL` terminated)
+ * @flags: Flags
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Create a new file, atomically replacing the contents of @subpath
+ * (relative to @dfd) with @buf.  By default, if the file already
+ * existed, fdatasync() will be used before rename() to ensure stable
+ * contents.  This and other behavior can be controlled via @flags.
+ *
+ * Note that no metadata from the existing file is preserved, such as
+ * uid/gid or extended attributes.  The default mode will be `0666`,
+ * modified by umask.
+ */ 
+gboolean
+glnx_file_replace_contents_at (int                   dfd,
+                               const char           *subpath,
+                               const guint8         *buf,
+                               gsize                 len,
+                               GLnxFileReplaceFlags  flags,
+                               GCancellable         *cancellable,
+                               GError              **error)
+{
+  return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len,
+                                                   (mode_t) -1, (uid_t) -1, (gid_t) -1,
+                                                   flags, cancellable, error);
+                                                   
+}
+
+/**
+ * glnx_file_replace_contents_with_perms_at:
+ * @dfd: Directory fd
+ * @subpath: Subpath
+ * @buf: (array len=len) (element-type guint8): File contents
+ * @len: Length (if `-1`, assume @buf is `NUL` terminated)
+ * @mode: File mode; if `-1`, use `0666 - umask`
+ * @flags: Flags
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Like glnx_file_replace_contents_at(), but also supports
+ * setting mode, and uid/gid.
+ */ 
+gboolean
+glnx_file_replace_contents_with_perms_at (int                   dfd,
+                                          const char           *subpath,
+                                          const guint8         *buf,
+                                          gsize                 len,
+                                          mode_t                mode,
+                                          uid_t                 uid,
+                                          gid_t                 gid,
+                                          GLnxFileReplaceFlags  flags,
+                                          GCancellable         *cancellable,
+                                          GError              **error)
+{
+  gboolean ret = FALSE;
+  int r;
+  /* We use the /proc/self trick as there's no mkostemp_at() yet */
+  g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd);
+  glnx_fd_close int fd = -1;
+
+  dfd = glnx_dirfd_canonicalize (dfd);
+
+  if ((fd = g_mkstemp_full (tmppath, O_WRONLY | O_CLOEXEC,
+                            mode == (mode_t) -1 ? 0666 : mode)) == -1)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  if (len == -1)
+    len = strlen ((char*)buf);
+
+  /* Note that posix_fallocate does *not* set errno but returns it. */
+  r = posix_fallocate (fd, 0, len);
+  if (r != 0)
+    {
+      errno = r;
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  if ((r = loop_write (fd, buf, len)) != 0)
+    {
+      errno = -r;
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+    
+  if (!(flags & GLNX_FILE_REPLACE_NODATASYNC))
+    {
+      struct stat stbuf;
+      gboolean do_sync;
+      
+      if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
+        {
+          if (errno != ENOENT)
+            {
+              glnx_set_error_from_errno (error);
+              goto out;
+            }
+          do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0;
+        }
+      else
+        do_sync = TRUE;
+
+      if (do_sync)
+        {
+          if (fdatasync (fd) != 0)
+            {
+              glnx_set_error_from_errno (error);
+              goto out;
+            }
+        }
+    }
+
+  if (uid != (uid_t) -1)
+    {
+      if (fchown (fd, uid, gid) != 0)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+
+  /* If a mode was forced, override umask */
+  if (mode != (mode_t) -1)
+    {
+      if (fchmod (fd, mode) != 0)
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+
+  if (renameat (dfd, tmppath, dfd, subpath) != 0)
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
diff --git a/libglnx/glnx-fdio.h b/libglnx/glnx-fdio.h
new file mode 100644 (file)
index 0000000..a90544a
--- /dev/null
@@ -0,0 +1,121 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/xattr.h>
+/* From systemd/src/shared/util.h */
+/* When we include libgen.h because we need dirname() we immediately
+ * undefine basename() since libgen.h defines it as a macro to the XDG
+ * version which is really broken. */
+#include <libgen.h>
+#undef basename
+
+G_BEGIN_DECLS
+
+/* Irritatingly, g_basename() which is what we want
+ * is deprecated.
+ */
+static inline
+const char *glnx_basename (const char *path)
+{
+  return (basename) (path);
+}
+
+GBytes *
+glnx_fd_readall_bytes (int               fd,
+                       GCancellable     *cancellable,
+                       GError          **error);
+
+char *
+glnx_fd_readall_utf8 (int               fd,
+                      gsize            *out_len,
+                      GCancellable     *cancellable,
+                      GError          **error);
+
+char *
+glnx_file_get_contents_utf8_at (int                   dfd,
+                                const char           *subpath,
+                                gsize                *out_len,
+                                GCancellable         *cancellable,
+                                GError              **error);
+
+/**
+ * GLnxFileReplaceFlags:
+ * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist
+ * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync()
+ *
+ * Flags controlling file replacement.
+ */
+typedef enum {
+  GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0),
+  GLNX_FILE_REPLACE_NODATASYNC = (1 << 1),
+} GLnxFileReplaceFlags;
+
+gboolean
+glnx_file_replace_contents_at (int                   dfd,
+                               const char           *subpath,
+                               const guint8         *buf,
+                               gsize                 len,
+                               GLnxFileReplaceFlags  flags,
+                               GCancellable         *cancellable,
+                               GError              **error);
+
+gboolean
+glnx_file_replace_contents_with_perms_at (int                   dfd,
+                                          const char           *subpath,
+                                          const guint8         *buf,
+                                          gsize                 len,
+                                          mode_t                mode,
+                                          uid_t                 uid,
+                                          gid_t                 gid,
+                                          GLnxFileReplaceFlags  flags,
+                                          GCancellable         *cancellable,
+                                          GError              **error);
+
+char *
+glnx_readlinkat_malloc (int            dfd,
+                        const char    *subpath,
+                        GCancellable  *cancellable,
+                        GError       **error);
+
+typedef enum {
+  GLNX_FILE_COPY_OVERWRITE,
+  GLNX_FILE_COPY_NOXATTRS,
+  GLNX_FILE_COPY_DATASYNC
+} GLnxFileCopyFlags;
+
+gboolean
+glnx_file_copy_at (int                   src_dfd,
+                   const char           *src_subpath,
+                   struct stat          *src_stbuf,
+                   int                   dest_dfd,
+                   const char           *dest_subpath,
+                   GLnxFileCopyFlags     copyflags,
+                   GCancellable         *cancellable,
+                   GError              **error);
+
+G_END_DECLS
diff --git a/libglnx/glnx-libcontainer.c b/libglnx/glnx-libcontainer.c
new file mode 100644 (file)
index 0000000..8c0f340
--- /dev/null
@@ -0,0 +1,292 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Portions derived from src/nspawn/nspawn.c:
+ *  Copyright 2010 Lennart Poettering
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib-unix.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <gio/gunixoutputstream.h>
+
+#include "glnx-libcontainer.h"
+
+#include "glnx-backport-autocleanups.h"
+#include "glnx-local-alloc.h"
+
+static void _perror_fatal (const char *message) __attribute__ ((noreturn));
+
+static void
+_perror_fatal (const char *message)
+{
+  perror (message);
+  exit (1);
+}
+
+typedef enum {
+  CONTAINER_UNINIT = 0,
+  CONTAINER_YES = 1,
+  CONTAINER_NO = 2
+} ContainerDetectionState;
+
+static gboolean
+currently_in_container (void)
+{
+  static gsize container_detected = CONTAINER_UNINIT;
+
+  if (g_once_init_enter (&container_detected))
+    {
+      ContainerDetectionState tmp_state = CONTAINER_NO;
+      struct stat stbuf;
+      
+      /* http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/ */
+      if (getenv ("container") != NULL
+          || stat ("/.dockerinit", &stbuf) == 0)
+        tmp_state = CONTAINER_YES;
+      /* But since Docker isn't on board, yet, so...
+         http://stackoverflow.com/questions/23513045/how-to-check-if-a-process-is-running-inside-docker-container */
+      g_once_init_leave (&container_detected, tmp_state);
+    }
+  return container_detected == CONTAINER_YES;
+}
+
+#if 0
+static gboolean
+glnx_libcontainer_bind_mount_readonly (const char *path, GError **error)
+{
+  gboolean ret = FALSE;
+
+  if (mount (path, path, NULL, MS_BIND | MS_PRIVATE, NULL) != 0)
+    {
+      int errsv = errno;
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "mount(%s, MS_BIND): %s",
+                   path,
+                   g_strerror (errsv));
+      goto out;
+    }
+  if (mount (path, path, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY, NULL) != 0)
+    {
+      int errsv = errno;
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "mount(%s, MS_BIND | MS_RDONLY): %s",
+                   path,
+                   g_strerror (errsv));
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+#endif
+
+/* Based on code from nspawn.c */
+static int
+glnx_libcontainer_make_api_mounts (const char *dest)
+{
+  typedef struct MountPoint {
+    const char *what;
+    const char *where;
+    const char *type;
+    const char *options;
+    unsigned long flags;
+    gboolean fatal;
+  } MountPoint;
+
+  static const MountPoint mount_table[] = {
+    { "proc",      "/proc",     "proc",  NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV,           TRUE  },
+    { "/proc/sys", "/proc/sys", NULL,    NULL,        MS_BIND,                                TRUE  },   /* Bind mount first */
+    { NULL,        "/proc/sys", NULL,    NULL,        MS_BIND|MS_RDONLY|MS_REMOUNT,           TRUE  },   /* Then, make it r/o */
+    { "sysfs",     "/sys",      "sysfs", NULL,        MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE  },
+    { "tmpfs",     "/dev",      "tmpfs", "mode=755",  MS_NOSUID|MS_STRICTATIME,               TRUE  },
+    { "devpts",    "/dev/pts",  "devpts","newinstance,ptmxmode=0666,mode=620,gid=5", MS_NOSUID|MS_NOEXEC, TRUE },
+    { "tmpfs",     "/dev/shm",  "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,      TRUE  },
+    { "tmpfs",     "/run",      "tmpfs", "mode=755",  MS_NOSUID|MS_NODEV|MS_STRICTATIME,      TRUE  },
+    { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND,                              FALSE },  /* Bind mount first */
+    { NULL,              "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT,         FALSE },  /* Then, make it r/o */
+  };
+
+  unsigned k;
+
+  for (k = 0; k < G_N_ELEMENTS(mount_table); k++)
+    {
+      g_autofree char *where = NULL;
+      int t;
+
+      where = g_build_filename (dest, mount_table[k].where, NULL);
+
+      t = mkdir (where, 0755);
+      if (t < 0 && errno != EEXIST)
+        {
+          if (!mount_table[k].fatal)
+            continue;
+          return -1;
+        }
+
+      if (mount (mount_table[k].what,
+                 where,
+                 mount_table[k].type,
+                 mount_table[k].flags,
+                 mount_table[k].options) < 0)
+        {
+          if (errno == ENOENT && !mount_table[k].fatal)
+            continue;
+          return -1;
+        }
+    }
+
+  return 0;
+}
+
+static int
+glnx_libcontainer_prep_dev (const char  *dest_devdir)
+{
+  glnx_fd_close int src_fd = -1;
+  glnx_fd_close int dest_fd = -1;
+  struct stat stbuf;
+  guint i;
+  static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" };
+
+  src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
+  if (src_fd == -1)
+    return -1;
+
+  dest_fd = openat (AT_FDCWD, dest_devdir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
+  if (dest_fd == -1)
+    return -1;
+
+  for (i = 0; i < G_N_ELEMENTS (devnodes); i++)
+    {
+      const char *nodename = devnodes[i];
+      
+      if (fstatat (src_fd, nodename, &stbuf, 0) == -1)
+        {
+          if (errno == ENOENT)
+            continue;
+          else
+            return -1;
+        }
+
+      if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0)
+        return -1;
+      if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0)
+        return -1;
+    }
+
+  return 0;
+}
+
+pid_t
+glnx_libcontainer_run_chroot_private (const char  *dest,
+                                      const char  *binary,
+                                      char **argv)
+{
+  /* Make most new namespaces; note our use of CLONE_NEWNET means we
+   * have no networking in the container root.
+   */
+  const int cloneflags = 
+    SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS;
+  pid_t child;
+  gboolean in_container = currently_in_container ();
+
+  if (!in_container)
+    {
+      if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0)
+        return -1;
+    }
+  else
+    {
+      if ((child = fork ()) < 0)
+        return -1;
+    }
+
+  if (child != 0)
+    return child;
+
+  if (!in_container)
+    {
+      if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0)
+        {
+          if (errno == EINVAL)
+            {
+              /* Ok, we may be inside a mock chroot or the like.  In
+               * that case, let's just fall back to not
+               * containerizing.
+               */
+              in_container = TRUE;
+            }
+          else
+            _perror_fatal ("mount: ");
+        }
+      
+      if (!in_container)
+        {
+          if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0)
+            _perror_fatal ("mount (MS_NOSUID): ");
+        }
+    }
+
+  if (chdir (dest) != 0)
+    _perror_fatal ("chdir: ");
+
+  if (!in_container)
+    {
+      if (glnx_libcontainer_make_api_mounts (dest) != 0)
+        _perror_fatal ("preparing api mounts: ");
+
+      if (glnx_libcontainer_prep_dev ("dev") != 0)
+        _perror_fatal ("preparing /dev: ");
+      
+      if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0)
+        _perror_fatal ("mount (MS_BIND)");
+      
+      if (mount (dest, "/", NULL, MS_MOVE, NULL) != 0)
+        _perror_fatal ("mount (MS_MOVE)");
+    }
+
+  if (chroot (".") != 0)
+    _perror_fatal ("chroot: ");
+
+  if (chdir ("/") != 0)
+    _perror_fatal ("chdir: ");
+
+  if (binary[0] == '/')
+    {
+      if (execv (binary, argv) != 0)
+        _perror_fatal ("execv: ");
+    }
+  else
+    {
+      /* Set PATH to something sane. */
+      setenv ("PATH", "/usr/sbin:/usr/bin", 1);
+
+      if (execvp (binary, argv) != 0)
+        _perror_fatal ("execvp: ");
+    }
+
+  g_assert_not_reached ();
+}
diff --git a/libglnx/glnx-libcontainer.h b/libglnx/glnx-libcontainer.h
new file mode 100644 (file)
index 0000000..10855db
--- /dev/null
@@ -0,0 +1,36 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/fsuid.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <sys/capability.h>
+#include <sched.h>
+
+pid_t glnx_libcontainer_run_chroot_private (const char  *dest,
+                                            const char  *binary,
+                                            char **argv);
diff --git a/libglnx/glnx-local-alloc.c b/libglnx/glnx-local-alloc.c
new file mode 100644 (file)
index 0000000..692f0de
--- /dev/null
@@ -0,0 +1,72 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012,2015 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "glnx-local-alloc.h"
+
+/**
+ * SECTION:glnxlocalalloc
+ * @title: GLnx local allocation
+ * @short_description: Release local variables automatically when they go out of scope
+ *
+ * These macros leverage the GCC extension __attribute__ ((cleanup))
+ * to allow calling a cleanup function such as g_free() when a
+ * variable goes out of scope.  See <ulink
+ * url="http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html">
+ * for more information on the attribute.
+ *
+ * The provided macros make it easy to use the cleanup attribute for
+ * types that come with GLib.  The primary two are #glnx_free and
+ * #glnx_unref_object, which correspond to g_free() and
+ * g_object_unref(), respectively.
+ *
+ * The rationale behind this is that particularly when handling error
+ * paths, it can be very tricky to ensure the right variables are
+ * freed.  With this, one simply applies glnx_unref_object to a
+ * locally-allocated #GFile for example, and it will be automatically
+ * unreferenced when it goes out of scope.
+ *
+ * Note - you should only use these macros for <emphasis>stack
+ * allocated</emphasis> variables.  They don't provide garbage
+ * collection or let you avoid freeing things.  They're simply a
+ * compiler assisted deterministic mechanism for calling a cleanup
+ * function when a stack frame ends.
+ *
+ * <example id="gs-lfree"><title>Calling g_free automatically</title>
+ * <programlisting>
+ *
+ * GFile *
+ * create_file (GError **error)
+ * {
+ *   glnx_free char *random_id = NULL;
+ *
+ *   if (!prepare_file (error))
+ *     return NULL;
+ *
+ *   random_id = alloc_random_id ();
+ *
+ *   return create_file_real (error);
+ *   // Note that random_id is freed here automatically
+ * }
+ * </programlisting>
+ * </example>
+ *
+ */
diff --git a/libglnx/glnx-local-alloc.h b/libglnx/glnx-local-alloc.h
new file mode 100644 (file)
index 0000000..af5af4b
--- /dev/null
@@ -0,0 +1,222 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GLNX_DEFINE_CLEANUP_FUNCTION(Type, name, func) \
+  static inline void name (void *v) \
+  { \
+    func (*(Type*)v); \
+  }
+
+#define GLNX_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \
+  static inline void name (void *v) \
+  { \
+    if (*(Type*)v) \
+      func (*(Type*)v); \
+  }
+
+/**
+ * glnx_free:
+ *
+ * Call g_free() on a variable location when it goes out of scope.
+ */
+#define glnx_free __attribute__ ((cleanup(glnx_local_free)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_free __attribute__ ((cleanup(glnx_local_free)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION(void*, glnx_local_free, g_free)
+
+/**
+ * glnx_unref_object:
+ *
+ * Call g_object_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_object_unref(), the variable may be
+ * %NULL.
+ */
+#define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_object __attribute__ ((cleanup(glnx_local_obj_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GObject*, glnx_local_obj_unref, g_object_unref)
+
+/**
+ * glnx_unref_variant:
+ *
+ * Call g_variant_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_variant_unref(), the variable may be
+ * %NULL.
+ */
+#define glnx_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GVariant*, glnx_local_variant_unref, g_variant_unref)
+
+/**
+ * glnx_free_variant_iter:
+ *
+ * Call g_variant_iter_free() on a variable location when it goes out of
+ * scope.
+ */
+#define glnx_free_variant_iter __attribute__ ((cleanup(glnx_local_variant_iter_free)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, glnx_local_variant_iter_free, g_variant_iter_free)
+
+/**
+ * glnx_free_variant_builder:
+ *
+ * Call g_variant_builder_unref() on a variable location when it goes out of
+ * scope.
+ */
+#define glnx_unref_variant_builder __attribute__ ((cleanup(glnx_local_variant_builder_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, glnx_local_variant_builder_unref, g_variant_builder_unref)
+
+/**
+ * glnx_unref_array:
+ *
+ * Call g_array_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define glnx_unref_array __attribute__ ((cleanup(glnx_local_array_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GArray*, glnx_local_array_unref, g_array_unref)
+
+/**
+ * glnx_unref_ptrarray:
+ *
+ * Call g_ptr_array_unref() on a variable location when it goes out of
+ * scope.  Note that unlike g_ptr_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define glnx_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, glnx_local_ptrarray_unref, g_ptr_array_unref)
+
+/**
+ * glnx_unref_hashtable:
+ *
+ * Call g_hash_table_unref() on a variable location when it goes out
+ * of scope.  Note that unlike g_hash_table_unref(), the variable may
+ * be %NULL.
+ */
+#define glnx_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref)))
+#ifdef GLNX_GSYSTEM_COMPAT
+#define gs_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref)))
+#endif
+GLNX_DEFINE_CLEANUP_FUNCTION0(GHashTable*, glnx_local_hashtable_unref, g_hash_table_unref)
+
+/**
+ * glnx_free_list:
+ *
+ * Call g_list_free() on a variable location when it goes out
+ * of scope.
+ */
+#define glnx_free_list __attribute__ ((cleanup(glnx_local_free_list)))
+GLNX_DEFINE_CLEANUP_FUNCTION(GList*, glnx_local_free_list, g_list_free)
+
+/**
+ * glnx_free_slist:
+ *
+ * Call g_slist_free() on a variable location when it goes out
+ * of scope.
+ */
+#define glnx_free_slist __attribute__ ((cleanup(glnx_local_free_slist)))
+GLNX_DEFINE_CLEANUP_FUNCTION(GSList*, glnx_local_free_slist, g_slist_free)
+
+/**
+ * glnx_free_checksum:
+ *
+ * Call g_checksum_free() on a variable location when it goes out
+ * of scope.  Note that unlike g_checksum_free(), the variable may
+ * be %NULL.
+ */
+#define glnx_free_checksum __attribute__ ((cleanup(glnx_local_checksum_free)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GChecksum*, glnx_local_checksum_free, g_checksum_free)
+
+/**
+ * glnx_unref_bytes:
+ *
+ * Call g_bytes_unref() on a variable location when it goes out
+ * of scope.  Note that unlike g_bytes_unref(), the variable may
+ * be %NULL.
+ */
+#define glnx_unref_bytes __attribute__ ((cleanup(glnx_local_bytes_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GBytes*, glnx_local_bytes_unref, g_bytes_unref)
+
+/**
+ * glnx_strfreev:
+ *
+ * Call g_strfreev() on a variable location when it goes out of scope.
+ */
+#define glnx_strfreev __attribute__ ((cleanup(glnx_local_strfreev)))
+GLNX_DEFINE_CLEANUP_FUNCTION(char**, glnx_local_strfreev, g_strfreev)
+
+/**
+ * glnx_free_error:
+ *
+ * Call g_error_free() on a variable location when it goes out of scope.
+ */
+#define glnx_free_error __attribute__ ((cleanup(glnx_local_free_error)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GError*, glnx_local_free_error, g_error_free)
+
+/**
+ * glnx_unref_keyfile:
+ *
+ * Call g_key_file_unref() on a variable location when it goes out of scope.
+ */
+#define glnx_unref_keyfile __attribute__ ((cleanup(glnx_local_keyfile_unref)))
+GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_unref)
+
+static inline void
+glnx_cleanup_close_fdp (int *fdp)
+{
+  int fd;
+
+  g_assert (fdp);
+  
+  fd = *fdp;
+  if (fd != -1)
+    (void) close (fd);
+}
+
+/**
+ * glnx_fd_close:
+ *
+ * Call close() on a variable location when it goes out of scope.
+ */
+#define glnx_fd_close __attribute__((cleanup(glnx_cleanup_close_fdp)))
+
+static inline int
+glnx_steal_fd (int *fdp)
+{
+  int fd = *fdp;
+  *fdp = -1;
+  return fd;
+}
+
+G_END_DECLS
diff --git a/libglnx/glnx-lockfile.c b/libglnx/glnx-lockfile.c
new file mode 100644 (file)
index 0000000..be9ad3a
--- /dev/null
@@ -0,0 +1,193 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+  Now copied into libglnx:
+    - Use GError
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2015 Colin Walters <walters@verbum.org>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "glnx-lockfile.h"
+#include "glnx-errors.h"
+#include "glnx-fdio.h"
+#include "glnx-backport-autocleanups.h"
+#include "glnx-local-alloc.h"
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+/**
+ * glnx_make_lock_file:
+ * @dfd: Directory file descriptor (if not `AT_FDCWD`, must have lifetime `>=` @out_lock)
+ * @p: Path
+ * @operation: one of `LOCK_SH`, `LOCK_EX`, `LOCK_UN`, as passed to flock()
+ * @out_lock: (out) (caller allocates): Return location for lock
+ * @error: Error
+ *
+ * Block until a lock file named @p (relative to @dfd) can be created,
+ * using the flags in @operation, returning the lock data in the
+ * caller-allocated location @out_lock.
+ *
+ * This API wraps new-style process locking if available, otherwise
+ * falls back to BSD locks.
+ */
+gboolean
+glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) {
+        gboolean ret = FALSE;
+        glnx_fd_close int fd = -1;
+        g_autofree char *t = NULL;
+        int r;
+
+        /*
+         * We use UNPOSIX locks if they are available. They have nice
+         * semantics, and are mostly compatible with NFS. However,
+         * they are only available on new kernels. When we detect we
+         * are running on an older kernel, then we fall back to good
+         * old BSD locks. They also have nice semantics, but are
+         * slightly problematic on NFS, where they are upgraded to
+         * POSIX locks, even though locally they are orthogonal to
+         * POSIX locks.
+         */
+
+        t = g_strdup(p);
+
+        for (;;) {
+#ifdef F_OFD_SETLK
+                struct flock fl = {
+                        .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK,
+                        .l_whence = SEEK_SET,
+                };
+#endif
+                struct stat st;
+
+                fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
+                if (fd < 0) {
+                        glnx_set_error_from_errno(error);
+                        goto out;
+                }
+
+                /* Unfortunately, new locks are not in RHEL 7.1 glibc */
+#ifdef F_OFD_SETLK
+                r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl);
+#else
+                r = -1;
+                errno = EINVAL;
+#endif
+                if (r < 0) {
+
+                        /* If the kernel is too old, use good old BSD locks */
+                        if (errno == EINVAL)
+                                r = flock(fd, operation);
+
+                        if (r < 0) {
+                                glnx_set_error_from_errno(error);
+                                goto out;
+                        }
+                }
+
+                /* If we acquired the lock, let's check if the file
+                 * still exists in the file system. If not, then the
+                 * previous exclusive owner removed it and then closed
+                 * it. In such a case our acquired lock is worthless,
+                 * hence try again. */
+
+                r = fstat(fd, &st);
+                if (r < 0) {
+                        glnx_set_error_from_errno(error);
+                        goto out;
+                }
+                if (st.st_nlink > 0)
+                        break;
+
+                (void) close(fd);
+                fd = -1;
+        }
+
+        /* Note that if this is not AT_FDCWD, the caller takes responsibility
+         * for the fd's lifetime being >= that of the lock.
+         */
+        out_lock->dfd = dfd;
+        out_lock->path = t;
+        out_lock->fd = fd;
+        out_lock->operation = operation;
+
+        fd = -1;
+        t = NULL;
+
+        ret = TRUE;
+ out:
+        return ret;
+}
+
+void glnx_release_lock_file(GLnxLockFile *f) {
+        int r;
+
+        if (!f)
+                return;
+
+        if (f->path) {
+
+                /* If we are the exclusive owner we can safely delete
+                 * the lock file itself. If we are not the exclusive
+                 * owner, we can try becoming it. */
+
+                if (f->fd >= 0 &&
+                    (f->operation & ~LOCK_NB) == LOCK_SH) {
+#ifdef F_OFD_SETLK
+                        static const struct flock fl = {
+                                .l_type = F_WRLCK,
+                                .l_whence = SEEK_SET,
+                        };
+
+                        r = fcntl(f->fd, F_OFD_SETLK, &fl);
+#else
+                        r = -1;
+                        errno = EINVAL;
+#endif
+                        if (r < 0 && errno == EINVAL)
+                                r = flock(f->fd, LOCK_EX|LOCK_NB);
+
+                        if (r >= 0)
+                                f->operation = LOCK_EX|LOCK_NB;
+                }
+
+                if ((f->operation & ~LOCK_NB) == LOCK_EX) {
+                        (void) unlinkat(f->dfd, f->path, 0);
+                }
+
+                g_free(f->path);
+                f->path = NULL;
+        }
+
+        (void) close (f->fd);
+        f->fd = -1;
+        f->operation = 0;
+}
diff --git a/libglnx/glnx-lockfile.h b/libglnx/glnx-lockfile.h
new file mode 100644 (file)
index 0000000..6d132e5
--- /dev/null
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+  Copyright 2015 Colin Walters <walters@verbum.org>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "config.h"
+
+#include "glnx-backport-autoptr.h"
+
+typedef struct GLnxLockFile {
+        int dfd;
+        char *path;
+        int fd;
+        int operation;
+} GLnxLockFile;
+
+gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *ret, GError **error);
+void glnx_release_lock_file(GLnxLockFile *f);
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxLockFile, glnx_release_lock_file)
+
+#define GLNX_LOCK_FILE_INIT { .fd = -1, .dfd = AT_FDCWD, .path = NULL }
diff --git a/libglnx/glnx-shutil.c b/libglnx/glnx-shutil.c
new file mode 100644 (file)
index 0000000..281e5bf
--- /dev/null
@@ -0,0 +1,287 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glnx-shutil.h>
+#include <glnx-errors.h>
+#include <glnx-local-alloc.h>
+
+static unsigned char
+struct_stat_to_dt (struct stat *stbuf)
+{
+  if (S_ISDIR (stbuf->st_mode))
+    return DT_DIR;
+  if (S_ISREG (stbuf->st_mode))
+    return DT_REG;
+  if (S_ISCHR (stbuf->st_mode))
+    return DT_CHR;
+  if (S_ISBLK (stbuf->st_mode))
+    return DT_BLK;
+  if (S_ISFIFO (stbuf->st_mode))
+    return DT_FIFO;
+  if (S_ISLNK (stbuf->st_mode))
+    return DT_LNK;
+  if (S_ISSOCK (stbuf->st_mode))
+    return DT_SOCK;
+  return DT_UNKNOWN;
+}
+
+static gboolean
+glnx_shutil_rm_rf_children (GLnxDirFdIterator    *dfd_iter,
+                            GCancellable       *cancellable,
+                            GError            **error)
+{
+  gboolean ret = FALSE;
+  struct dirent *dent;
+
+  while (TRUE)
+    {
+      if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, cancellable, error))
+        goto out;
+
+      if (dent == NULL)
+        break;
+
+      if (dent->d_type == DT_UNKNOWN)
+        {
+          struct stat stbuf;
+          if (fstatat (dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
+            {
+              if (errno == ENOENT)
+                continue;
+              else
+                {
+                  glnx_set_error_from_errno (error);
+                  goto out;
+                }
+            }
+          dent->d_type = struct_stat_to_dt (&stbuf);
+          /* Assume unknown types are just treated like regular files */
+          if (dent->d_type == DT_UNKNOWN)
+            dent->d_type = DT_REG;
+        }
+
+      if (dent->d_type == DT_DIR)
+        {
+          g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, };
+
+          if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE,
+                                            &child_dfd_iter, error))
+            goto out;
+
+          if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error))
+            goto out;
+
+          if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1)
+            {
+              glnx_set_error_from_errno (error);
+              goto out;
+            }
+        }
+      else
+        {
+          if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1)
+            {
+              if (errno != ENOENT)
+                {
+                  glnx_set_error_from_errno (error);
+                  goto out;
+                }
+            }
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_shutil_rm_rf_at:
+ * @dfd: A directory file descriptor, or `AT_FDCWD` or `-1` for current
+ * @path: Path
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Recursively delete the filename referenced by the combination of
+ * the directory fd @dfd and @path; it may be a file or directory.  No
+ * error is thrown if @path does not exist.
+ */
+gboolean
+glnx_shutil_rm_rf_at (int                   dfd,
+                      const char           *path,
+                      GCancellable         *cancellable,
+                      GError              **error)
+{
+  gboolean ret = FALSE;
+  glnx_fd_close int target_dfd = -1;
+  g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+
+  dfd = glnx_dirfd_canonicalize (dfd);
+
+  /* With O_NOFOLLOW first */
+  target_dfd = openat (dfd, path,
+                       O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
+
+  if (target_dfd == -1)
+    {
+      int errsv = errno;
+      if (errsv == ENOENT)
+        {
+          ;
+        }
+      else if (errsv == ENOTDIR || errsv == ELOOP)
+        {
+          if (unlinkat (dfd, path, 0) != 0)
+            {
+              glnx_set_error_from_errno (error);
+              goto out;
+            }
+        }
+      else
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+  else
+    {
+      if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error))
+        goto out;
+      target_dfd = -1;
+
+      if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error))
+        goto out;
+
+      if (unlinkat (dfd, path, AT_REMOVEDIR) == -1)
+        {
+          int errsv = errno;
+          if (errsv != ENOENT)
+            {
+              glnx_set_error_from_errno (error);
+              goto out;
+            }
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+mkdir_p_at_internal (int              dfd,
+                     char            *path,
+                     int              mode,
+                     GCancellable    *cancellable,
+                     GError         **error)
+{
+  gboolean ret = FALSE;
+  gboolean did_recurse = FALSE;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    goto out;
+
+ again:
+  if (mkdirat (dfd, path, mode) == -1)
+    {
+      if (errno == ENOENT)
+        {
+          char *lastslash;
+
+          g_assert (!did_recurse);
+
+          lastslash = strrchr (path, '/');
+          g_assert (lastslash != NULL);
+          /* Note we can mutate the buffer as we dup'd it */
+          *lastslash = '\0';
+
+          if (!glnx_shutil_mkdir_p_at (dfd, path, mode,
+                                       cancellable, error))
+            goto out;
+
+          /* Now restore it for another mkdir attempt */
+          *lastslash = '/';
+
+          did_recurse = TRUE;
+          goto again;
+        }
+      else if (errno == EEXIST)
+        {
+          /* Fall through; it may not have been a directory,
+           * but we'll find that out on the next call up.
+           */
+        }
+      else
+        {
+          glnx_set_error_from_errno (error);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_shutil_mkdir_p_at:
+ * @dfd: Directory fd
+ * @path: Directory path to be created
+ * @mode: Mode for newly created directories
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Similar to g_mkdir_with_parents(), except operates relative to the
+ * directory fd @dfd.
+ */
+gboolean
+glnx_shutil_mkdir_p_at (int                   dfd,
+                        const char           *path,
+                        int                   mode,
+                        GCancellable         *cancellable,
+                        GError              **error)
+{
+  gboolean ret = FALSE;
+  struct stat stbuf;
+  char *buf;
+
+  /* Fast path stat to see whether it already exists */
+  if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
+    {
+      if (S_ISDIR (stbuf.st_mode))
+        {
+          ret = TRUE;
+          goto out;
+        }
+    }
+
+  buf = strdupa (path);
+
+  if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
diff --git a/libglnx/glnx-shutil.h b/libglnx/glnx-shutil.h
new file mode 100644 (file)
index 0000000..8cc732c
--- /dev/null
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-dirfd.h>
+
+G_BEGIN_DECLS
+
+gboolean
+glnx_shutil_rm_rf_at (int                   dfd,
+                      const char           *path,
+                      GCancellable         *cancellable,
+                      GError              **error);
+
+gboolean
+glnx_shutil_mkdir_p_at (int                   dfd,
+                        const char           *path,
+                        int                   mode,
+                        GCancellable         *cancellable,
+                        GError              **error);
+
+G_END_DECLS
diff --git a/libglnx/glnx-xattrs.c b/libglnx/glnx-xattrs.c
new file mode 100644 (file)
index 0000000..b5a5922
--- /dev/null
@@ -0,0 +1,526 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glnx-xattrs.h>
+#include <glnx-errors.h>
+#include <glnx-local-alloc.h>
+
+static GVariant *
+variant_new_ay_bytes (GBytes *bytes)
+{
+  gsize size;
+  gconstpointer data;
+  data = g_bytes_get_data (bytes, &size);
+  g_bytes_ref (bytes);
+  return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data, size,
+                                  TRUE, (GDestroyNotify)g_bytes_unref, bytes);
+}
+
+static char *
+canonicalize_xattrs (char    *xattr_string,
+                     size_t   len)
+{
+  char *p;
+  GSList *xattrs = NULL;
+  GSList *iter;
+  GString *result;
+
+  result = g_string_new (0);
+
+  p = xattr_string;
+  while (p < xattr_string+len)
+    {
+      xattrs = g_slist_prepend (xattrs, p);
+      p += strlen (p) + 1;
+    }
+
+  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+  for (iter = xattrs; iter; iter = iter->next) {
+    g_string_append (result, iter->data);
+    g_string_append_c (result, '\0');
+  }
+
+  g_slist_free (xattrs);
+  return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+                       int         fd,
+                       const char *xattrs,
+                       size_t      len,
+                       GVariantBuilder *builder,
+                       GError  **error)
+{
+  gboolean ret = FALSE;
+  const char *p;
+  int r;
+  const char *funcstr;
+
+  g_assert (path != NULL || fd != -1);
+
+  funcstr = fd != -1 ? "fgetxattr" : "lgetxattr";
+
+  p = xattrs;
+  while (p < xattrs+len)
+    {
+      ssize_t bytes_read;
+      char *buf;
+      GBytes *bytes = NULL;
+
+      if (fd != -1)
+        bytes_read = fgetxattr (fd, p, NULL, 0);
+      else
+        bytes_read = lgetxattr (path, p, NULL, 0);
+      if (bytes_read < 0)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", funcstr);
+          goto out;
+        }
+      if (bytes_read == 0)
+        continue;
+
+      buf = g_malloc (bytes_read);
+      bytes = g_bytes_new_take (buf, bytes_read);
+      if (fd != -1)
+        r = fgetxattr (fd, p, buf, bytes_read);
+      else
+        r = lgetxattr (path, p, buf, bytes_read);
+      if (r < 0)
+        {
+          g_bytes_unref (bytes);
+          glnx_set_prefix_error_from_errno (error, "%s", funcstr);
+          goto out;
+        }
+      
+      g_variant_builder_add (builder, "(@ay@ay)",
+                             g_variant_new_bytestring (p),
+                             variant_new_ay_bytes (bytes));
+
+      p = p + strlen (p) + 1;
+      g_bytes_unref (bytes);
+    }
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+get_xattrs_impl (const char      *path,
+                 GVariant       **out_xattrs,
+                 GCancellable   *cancellable,
+                 GError        **error)
+{
+  gboolean ret = FALSE;
+  ssize_t bytes_read;
+  glnx_free char *xattr_names = NULL;
+  glnx_free char *xattr_names_canonical = NULL;
+  GVariantBuilder builder;
+  gboolean builder_initialized = FALSE;
+  g_autoptr(GVariant) ret_xattrs = NULL;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+  builder_initialized = TRUE;
+
+  bytes_read = llistxattr (path, NULL, 0);
+
+  if (bytes_read < 0)
+    {
+      if (errno != ENOTSUP)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "llistxattr");
+          goto out;
+        }
+    }
+  else if (bytes_read > 0)
+    {
+      xattr_names = g_malloc (bytes_read);
+      if (llistxattr (path, xattr_names, bytes_read) < 0)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "llistxattr");
+          goto out;
+        }
+      xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+      
+      if (!read_xattr_name_array (path, -1, xattr_names_canonical, bytes_read, &builder, error))
+        goto out;
+    }
+
+  ret_xattrs = g_variant_builder_end (&builder);
+  builder_initialized = FALSE;
+  g_variant_ref_sink (ret_xattrs);
+  
+  ret = TRUE;
+  if (out_xattrs)
+    *out_xattrs = g_steal_pointer (&ret_xattrs);
+ out:
+  if (!builder_initialized)
+    g_variant_builder_clear (&builder);
+  return ret;
+}
+
+/**
+ * glnx_fd_get_all_xattrs:
+ * @fd: a file descriptor
+ * @out_xattrs: (out): A new #GVariant containing the extended attributes
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Read all extended attributes from @fd in a canonical sorted order, and
+ * set @out_xattrs with the result.
+ *
+ * If the filesystem does not support extended attributes, @out_xattrs
+ * will have 0 elements, and this function will return successfully.
+ */
+gboolean
+glnx_fd_get_all_xattrs (int            fd,
+                        GVariant     **out_xattrs,
+                        GCancellable  *cancellable,
+                        GError       **error)
+{
+  gboolean ret = FALSE;
+  ssize_t bytes_read;
+  glnx_free char *xattr_names = NULL;
+  glnx_free char *xattr_names_canonical = NULL;
+  GVariantBuilder builder;
+  gboolean builder_initialized = FALSE;
+  g_autoptr(GVariant) ret_xattrs = NULL;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+  builder_initialized = TRUE;
+
+  bytes_read = flistxattr (fd, NULL, 0);
+
+  if (bytes_read < 0)
+    {
+      if (errno != ENOTSUP)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "flistxattr");
+          goto out;
+        }
+    }
+  else if (bytes_read > 0)
+    {
+      xattr_names = g_malloc (bytes_read);
+      if (flistxattr (fd, xattr_names, bytes_read) < 0)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "flistxattr");
+          goto out;
+        }
+      xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+      
+      if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, bytes_read, &builder, error))
+        goto out;
+    }
+
+  ret_xattrs = g_variant_builder_end (&builder);
+  builder_initialized = FALSE;
+  g_variant_ref_sink (ret_xattrs);
+  
+  ret = TRUE;
+  if (out_xattrs)
+    *out_xattrs = g_steal_pointer (&ret_xattrs);
+ out:
+  if (!builder_initialized)
+    g_variant_builder_clear (&builder);
+  return ret;
+}
+
+/**
+ * glnx_dfd_name_get_all_xattrs:
+ * @dfd: Parent directory file descriptor
+ * @name: File name
+ * @out_xattrs: (out): Extended attribute set
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Load all extended attributes for the file named @name residing in
+ * directory @dfd.
+ */
+gboolean
+glnx_dfd_name_get_all_xattrs (int            dfd,
+                              const char    *name,
+                              GVariant     **out_xattrs,
+                              GCancellable  *cancellable,
+                              GError       **error)
+{
+  if (dfd == AT_FDCWD || dfd == -1)
+    {
+      return get_xattrs_impl (name, out_xattrs, cancellable, error);
+    }
+  else
+    {
+      char buf[PATH_MAX];
+      /* A workaround for the lack of lgetxattrat(), thanks to Florian Weimer:
+       * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html
+       */
+      snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name);
+      return get_xattrs_impl (buf, out_xattrs, cancellable, error);
+    }
+}
+
+static gboolean
+set_all_xattrs_for_path (const char    *path,
+                         GVariant      *xattrs,
+                         GCancellable  *cancellable,
+                         GError       **error)
+{
+  gboolean ret = FALSE;
+  int i, n;
+
+  n = g_variant_n_children (xattrs);
+  for (i = 0; i < n; i++)
+    {
+      const guint8* name;
+      g_autoptr(GVariant) value = NULL;
+      const guint8* value_data;
+      gsize value_len;
+
+      g_variant_get_child (xattrs, i, "(^&ay@ay)",
+                           &name, &value);
+      value_data = g_variant_get_fixed_array (value, &value_len, 1);
+      
+      if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0)
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "lsetxattr");
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_dfd_name_set_all_xattrs:
+ * @dfd: Parent directory file descriptor
+ * @name: File name
+ * @xattrs: Extended attribute set
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Set all extended attributes for the file named @name residing in
+ * directory @dfd.
+ */
+gboolean
+glnx_dfd_name_set_all_xattrs (int            dfd,
+                              const char    *name,
+                              GVariant      *xattrs,
+                              GCancellable  *cancellable,
+                              GError       **error)
+{
+  if (dfd == AT_FDCWD || dfd == -1)
+    {
+      return set_all_xattrs_for_path (name, xattrs, cancellable, error);
+    }
+  else
+    {
+      char buf[PATH_MAX];
+      /* A workaround for the lack of lsetxattrat(), thanks to Florian Weimer:
+       * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html
+       */
+      snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name);
+      return set_all_xattrs_for_path (buf, xattrs, cancellable, error);
+    }
+}
+
+/**
+ * glnx_fd_set_all_xattrs:
+ * @fd: File descriptor
+ * @xattrs: Extended attributes
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * For each attribute in @xattrs, set its value on the file or
+ * directory referred to by @fd.  This function does not remove any
+ * attributes not in @xattrs.
+ */
+gboolean
+glnx_fd_set_all_xattrs (int            fd,
+                        GVariant      *xattrs,
+                        GCancellable  *cancellable,
+                        GError       **error)
+{
+  gboolean ret = FALSE;
+  int i, n;
+
+  n = g_variant_n_children (xattrs);
+  for (i = 0; i < n; i++)
+    {
+      const guint8* name;
+      const guint8* value_data;
+      g_autoptr(GVariant) value = NULL;
+      gsize value_len;
+      int res;
+
+      g_variant_get_child (xattrs, i, "(^&ay@ay)",
+                           &name, &value);
+      value_data = g_variant_get_fixed_array (value, &value_len, 1);
+      
+      do
+        res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0);
+      while (G_UNLIKELY (res == -1 && errno == EINTR));
+      if (G_UNLIKELY (res == -1))
+        {
+          glnx_set_prefix_error_from_errno (error, "%s", "fsetxattr");
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * glnx_lgetxattrat:
+ * @dfd: Directory file descriptor
+ * @subpath: Subpath
+ * @attribute: Extended attribute to retrieve
+ * @error: Error
+ *
+ * Retrieve an extended attribute value, relative to a directory file
+ * descriptor.
+ */
+GBytes *
+glnx_lgetxattrat (int            dfd,
+                  const char    *subpath,
+                  const char    *attribute,
+                  GError       **error)
+{
+  char pathbuf[PATH_MAX];
+  GBytes *bytes = NULL;
+  ssize_t bytes_read, real_size;
+  guint8 *buf;
+
+  snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath);
+
+  do
+    bytes_read = lgetxattr (pathbuf, attribute, NULL, 0);
+  while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
+  if (G_UNLIKELY (bytes_read < 0))
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  buf = g_malloc (bytes_read);
+  do
+    real_size = lgetxattr (pathbuf, attribute, buf, bytes_read);
+  while (G_UNLIKELY (real_size < 0 && errno == EINTR));
+  if (G_UNLIKELY (real_size < 0))
+    {
+      glnx_set_error_from_errno (error);
+      g_free (buf);
+      goto out;
+    }
+
+  bytes = g_bytes_new_take (buf, real_size);
+ out:
+  return bytes;
+}
+
+/**
+ * glnx_fgetxattr_bytes:
+ * @fd: Directory file descriptor
+ * @attribute: Extended attribute to retrieve
+ * @error: Error
+ *
+ * Returns: (transfer full): An extended attribute value, or %NULL on error
+ */
+GBytes *
+glnx_fgetxattr_bytes (int            fd,
+                      const char    *attribute,
+                      GError       **error)
+{
+  GBytes *bytes = NULL;
+  ssize_t bytes_read, real_size;
+  guint8 *buf;
+
+  do
+    bytes_read = fgetxattr (fd, attribute, NULL, 0);
+  while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
+  if (G_UNLIKELY (bytes_read < 0))
+    {
+      glnx_set_error_from_errno (error);
+      goto out;
+    }
+
+  buf = g_malloc (bytes_read);
+  do
+    real_size = fgetxattr (fd, attribute, buf, bytes_read);
+  while (G_UNLIKELY (real_size < 0 && errno == EINTR));
+  if (G_UNLIKELY (real_size < 0))
+    {
+      glnx_set_error_from_errno (error);
+      g_free (buf);
+      goto out;
+    }
+
+  bytes = g_bytes_new_take (buf, real_size);
+ out:
+  return bytes;
+}
+
+/**
+ * glnx_lsetxattrat:
+ * @dfd: Directory file descriptor
+ * @subpath: Path
+ * @attribute: An attribute name
+ * @value: (array length=len) (element-type guint8): Attribute value
+ * @len: Length of @value
+ * @flags: Flags, containing either XATTR_CREATE or XATTR_REPLACE
+ * @error: Error
+ *
+ * Set an extended attribute, relative to a directory file descriptor.
+ */
+gboolean
+glnx_lsetxattrat (int            dfd,
+                  const char    *subpath,
+                  const char    *attribute,
+                  const guint8  *value,
+                  gsize          len,
+                  int            flags,
+                  GError       **error)
+{
+  char pathbuf[PATH_MAX];
+  int res;
+
+  snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath);
+
+  do
+    res = lsetxattr (subpath, attribute, value, len, flags);
+  while (G_UNLIKELY (res == -1 && errno == EINTR));
+  if (G_UNLIKELY (res == -1))
+    {
+      glnx_set_error_from_errno (error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
diff --git a/libglnx/glnx-xattrs.h b/libglnx/glnx-xattrs.h
new file mode 100644 (file)
index 0000000..a566a22
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <glnx-backport-autocleanups.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/xattr.h>
+
+G_BEGIN_DECLS
+
+gboolean
+glnx_dfd_name_get_all_xattrs (int                    dfd,
+                              const char            *name,
+                              GVariant             **out_xattrs,
+                              GCancellable          *cancellable,
+                              GError               **error);
+
+gboolean
+glnx_fd_get_all_xattrs (int                    fd,
+                        GVariant             **out_xattrs,
+                        GCancellable          *cancellable,
+                        GError               **error);
+
+gboolean
+glnx_dfd_name_set_all_xattrs (int            dfd,
+                              const char    *name,
+                              GVariant      *xattrs,
+                              GCancellable  *cancellable,
+                              GError       **error);
+
+gboolean
+glnx_fd_set_all_xattrs (int            fd,
+                        GVariant      *xattrs,
+                        GCancellable  *cancellable,
+                        GError       **error);
+
+GBytes *
+glnx_lgetxattrat (int            dfd,
+                  const char    *subpath,
+                  const char    *attribute,
+                  GError       **error);
+
+GBytes *
+glnx_fgetxattr_bytes (int            fd,
+                      const char    *attribute,
+                      GError       **error);
+
+gboolean
+glnx_lsetxattrat (int            dfd,
+                  const char    *subpath,
+                  const char    *attribute,
+                  const guint8  *value,
+                  gsize          len,
+                  int            flags,
+                  GError       **error);
+
+G_END_DECLS
diff --git a/libglnx/libglnx.doap b/libglnx/libglnx.doap
new file mode 100644 (file)
index 0000000..2b38074
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+         xmlns:foaf="http://xmlns.com/foaf/0.1/"
+         xmlns:gnome="http://api.gnome.org/doap-extensions#"
+         xmlns="http://usefulinc.com/ns/doap#">
+
+  <name>libglnx</name>
+  <shortname>libglnx</shortname>
+
+  <shortdesc xml:lang="en">"Copylib" for system service modules using GLib with Linux</shortdesc>
+
+  <description xml:lang="en">This module is intended for use by
+  infrastructure code using GLib that is also Linux specific, such as
+  ostree, NetworkManager, and others.
+  </description>
+
+  <license rdf:resource="http://usefulinc.com/doap/licenses/lgpl" />
+  <mailing-list rdf:resource="mailto:desktop-devel-list@gnome.org" />
+
+  <programming-language>C</programming-language>
+
+  <maintainer>
+    <foaf:Person>
+      <foaf:name>Colin Walters</foaf:name>
+      <foaf:mbox rdf:resource="mailto:walters@verbum.org"/>
+      <gnome:userid>walters</gnome:userid>
+    </foaf:Person>
+  </maintainer>
+
+</Project>
diff --git a/libglnx/libglnx.h b/libglnx/libglnx.h
new file mode 100644 (file)
index 0000000..9bd41c4
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012,2013,2015 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#include <glnx-local-alloc.h>
+#include <glnx-backport-autocleanups.h>
+#include <glnx-backports.h>
+#include <glnx-lockfile.h>
+#include <glnx-errors.h>
+#include <glnx-dirfd.h>
+#include <glnx-shutil.h>
+#include <glnx-xattrs.h>
+#include <glnx-libcontainer.h>
+#include <glnx-console.h>
+#include <glnx-fdio.h>
+
+G_END_DECLS